From 31332c518b02bd7260c1aad0d8d545275a678bd1 Mon Sep 17 00:00:00 2001 From: Viswamedha Nalabotu Date: Sun, 8 Mar 2026 12:19:36 +0000 Subject: [PATCH] Tweaked indent, added 2 user methods and 2 receivers for organization --- apps/accounts/models.py | 75 ++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/apps/accounts/models.py b/apps/accounts/models.py index 69c8aea..96fa4f9 100644 --- a/apps/accounts/models.py +++ b/apps/accounts/models.py @@ -1,11 +1,11 @@ from typing import ClassVar -from uuid import uuid4 from datetime import timedelta from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin +from django.core.exceptions import ValidationError from django.db import transaction -from django.db.models import BooleanField, CASCADE, CharField, DateField, DateTimeField, EmailField, ForeignKey, IntegerField, ManyToManyField, Model, TextField, UUIDField -from django.db.models.signals import post_delete, post_save +from django.db.models import BooleanField, CASCADE, CharField, DateField, DateTimeField, EmailField, ForeignKey, IntegerField, ManyToManyField, Model, TextField +from django.db.models.signals import m2m_changed, post_delete, post_save from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from django.utils import timezone @@ -15,37 +15,43 @@ from apps.accounts.mixins import IdentifierMixin, TimeStampMixin class User(AbstractBaseUser, IdentifierMixin, TimeStampMixin, PermissionsMixin): - email_address = EmailField(verbose_name = _("Email Address"), max_length = 255, unique = True) - first_name = CharField(verbose_name = _("First Name"), max_length = 255) - last_name = CharField(verbose_name = _("Last Name"), max_length = 255) - date_of_birth = DateField(verbose_name = _("Date of Birth"), null = True, blank = True) + email_address = EmailField(verbose_name = _("Email Address"), max_length = 255, unique = True) + first_name = CharField(verbose_name = _("First Name"), max_length = 255) + last_name = CharField(verbose_name = _("Last Name"), max_length = 255) + date_of_birth = DateField(verbose_name = _("Date of Birth"), null = True, blank = True) - is_active = BooleanField(verbose_name = _("Account Active"), default = True) - is_staff = BooleanField(verbose_name = _("Account Admin"), default = False) - is_manager = BooleanField(verbose_name = _("Organization Manager"), default = False) + is_active = BooleanField(verbose_name = _("Account Active"), default = True) + is_staff = BooleanField(verbose_name = _("Account Admin"), default = False) + is_manager = BooleanField(verbose_name = _("Organization Manager"), default = False) - USERNAME_FIELD = 'email_address' - EMAIL_FIELD = 'email_address' - REQUIRED_FIELDS = ['first_name', 'last_name', 'date_of_birth'] + USERNAME_FIELD = 'email_address' + EMAIL_FIELD = 'email_address' + REQUIRED_FIELDS = ['first_name', 'last_name', 'date_of_birth'] - objects: ClassVar[UserManager] = UserManager() + objects: ClassVar[UserManager] = UserManager() - def has_perm(self, perm, obj=None): - return True + def has_perm(self, perm, obj=None): + return True - def has_module_perms(self, app_label): - return True + def has_module_perms(self, app_label): + return True - class Meta: - verbose_name = _('User') - verbose_name_plural = _('Users') + class Meta: + verbose_name = _('User') + verbose_name_plural = _('Users') - @property - def full_name(self) -> str: - return f"{self.first_name} {self.last_name}" + @property + def full_name(self) -> str: + return f"{self.first_name} {self.last_name}" - def __str__(self) -> str: - return self.full_name + def is_owner_of(self, organization: 'Organization') -> bool: + return organization.owner.id == self.id + + def is_member_of(self, organization: 'Organization') -> bool: + return organization.members.filter(id=self.id).exists() + + def __str__(self) -> str: + return self.full_name class Organization(IdentifierMixin, TimeStampMixin, Model): @@ -62,7 +68,7 @@ class Organization(IdentifierMixin, TimeStampMixin, Model): def __str__(self) -> str: return self.name - + class Invite(IdentifierMixin, TimeStampMixin, Model): organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "invites") @@ -165,3 +171,18 @@ def create_default_agents_for_role(sender, instance: Role, created: bool, **kwar def delete_role_agents_on_role_delete(sender, instance: Role, **kwargs): from apps.onboarding.models import AgentConfig AgentConfig.objects.filter(role=instance).delete() + +@receiver(post_save, sender=Organization) +def ensure_owner_is_member(sender, instance: Organization, **kwargs): + if instance.owner.id and not instance.members.filter(id=instance.owner.id).exists(): + instance.members.add(instance.owner) + +@receiver(m2m_changed, sender=Organization.members.through) +def prevent_owner_from_being_removed(sender, instance: Organization, action: str, pk_set, **kwargs): + if action == 'pre_remove' and instance.owner.id in (pk_set or set()): + raise ValidationError(_('Organization owner must remain a member.')) + + if action in ['post_add', 'post_remove', 'post_clear']: + if instance.owner.id and not instance.members.filter(id=instance.owner.id).exists(): + instance.members.add(instance.owner) + \ No newline at end of file