users.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. from django.contrib.auth.base_user import AbstractBaseUser
  2. from django.contrib.auth.models import (
  3. GroupManager as DjangoGroupManager,
  4. Permission,
  5. PermissionsMixin,
  6. UserManager as DjangoUserManager,
  7. )
  8. from django.contrib.auth.validators import UnicodeUsernameValidator
  9. from django.core.exceptions import ValidationError
  10. from django.core.mail import send_mail
  11. from django.db import models
  12. from django.urls import reverse
  13. from django.utils import timezone
  14. from django.utils.translation import gettext_lazy as _
  15. from utilities.querysets import RestrictedQuerySet
  16. __all__ = (
  17. 'Group',
  18. 'GroupManager',
  19. 'User',
  20. 'UserManager',
  21. )
  22. class GroupManager(DjangoGroupManager.from_queryset(RestrictedQuerySet)):
  23. pass
  24. class Group(models.Model):
  25. name = models.CharField(
  26. verbose_name=_('name'),
  27. max_length=150,
  28. unique=True
  29. )
  30. description = models.CharField(
  31. verbose_name=_('description'),
  32. max_length=200,
  33. blank=True
  34. )
  35. object_permissions = models.ManyToManyField(
  36. to='users.ObjectPermission',
  37. blank=True,
  38. related_name='groups'
  39. )
  40. # Replicate legacy Django permissions support from stock Group model
  41. # to ensure authentication backend compatibility
  42. permissions = models.ManyToManyField(
  43. Permission,
  44. verbose_name=_("permissions"),
  45. blank=True,
  46. related_name='groups',
  47. related_query_name='group'
  48. )
  49. objects = GroupManager()
  50. class Meta:
  51. ordering = ('name',)
  52. verbose_name = _('group')
  53. verbose_name_plural = _('groups')
  54. def __str__(self):
  55. return self.name
  56. def get_absolute_url(self):
  57. return reverse('users:group', args=[self.pk])
  58. def natural_key(self):
  59. return (self.name,)
  60. class UserManager(DjangoUserManager.from_queryset(RestrictedQuerySet)):
  61. def create_user(self, username, email=None, password=None, **extra_fields):
  62. extra_fields.setdefault('is_superuser', False)
  63. return self._create_user(username, email, password, **extra_fields)
  64. create_user.alters_data = True
  65. async def acreate_user(self, username, email=None, password=None, **extra_fields):
  66. extra_fields.setdefault('is_superuser', False)
  67. return await self._acreate_user(username, email, password, **extra_fields)
  68. acreate_user.alters_data = True
  69. def create_superuser(self, username, email=None, password=None, **extra_fields):
  70. extra_fields.setdefault('is_superuser', True)
  71. if extra_fields.get('is_superuser') is not True:
  72. raise ValueError('Superuser must have is_superuser=True.')
  73. return self._create_user(username, email, password, **extra_fields)
  74. create_superuser.alters_data = True
  75. async def acreate_superuser(self, username, email=None, password=None, **extra_fields):
  76. extra_fields.setdefault('is_superuser', True)
  77. if extra_fields.get('is_superuser') is not True:
  78. raise ValueError('Superuser must have is_superuser=True.')
  79. return await self._acreate_user(username, email, password, **extra_fields)
  80. acreate_superuser.alters_data = True
  81. class User(AbstractBaseUser, PermissionsMixin):
  82. username = models.CharField(
  83. _("username"),
  84. max_length=150,
  85. unique=True,
  86. help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."),
  87. validators=[UnicodeUsernameValidator()],
  88. error_messages={
  89. "unique": _("A user with that username already exists."),
  90. },
  91. )
  92. first_name = models.CharField(
  93. _("first name"),
  94. max_length=150,
  95. blank=True,
  96. )
  97. last_name = models.CharField(
  98. _("last name"),
  99. max_length=150,
  100. blank=True,
  101. )
  102. email = models.EmailField(
  103. _("email address"),
  104. blank=True,
  105. )
  106. is_active = models.BooleanField(
  107. _("active"),
  108. default=True,
  109. help_text=_(
  110. "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
  111. ),
  112. )
  113. date_joined = models.DateTimeField(
  114. _("date joined"),
  115. default=timezone.now,
  116. )
  117. groups = models.ManyToManyField(
  118. to='users.Group',
  119. verbose_name=_('groups'),
  120. blank=True,
  121. related_name='users',
  122. related_query_name='user'
  123. )
  124. object_permissions = models.ManyToManyField(
  125. to='users.ObjectPermission',
  126. blank=True,
  127. related_name='users'
  128. )
  129. objects = UserManager()
  130. # Ensure compatibility with Django's stock User model
  131. EMAIL_FIELD = "email"
  132. USERNAME_FIELD = "username"
  133. REQUIRED_FIELDS = ["email"]
  134. class Meta:
  135. ordering = ('username',)
  136. verbose_name = _('user')
  137. verbose_name_plural = _('users')
  138. def get_absolute_url(self):
  139. return reverse('users:user', args=[self.pk])
  140. def clean(self):
  141. super().clean()
  142. # Normalize email address
  143. self.email = self.__class__.objects.normalize_email(self.email)
  144. # Check for any existing Users with names that differ only in case
  145. model = self._meta.model
  146. if model.objects.exclude(pk=self.pk).filter(username__iexact=self.username).exists():
  147. raise ValidationError(_("A user with this username already exists."))
  148. def get_full_name(self):
  149. """
  150. Return the first_name plus the last_name, with a space in between.
  151. """
  152. full_name = "%s %s" % (self.first_name, self.last_name)
  153. return full_name.strip()
  154. def get_short_name(self):
  155. """Return the short name for the user."""
  156. return self.first_name
  157. def email_user(self, subject, message, from_email=None, **kwargs):
  158. """Send an email to this user."""
  159. send_mail(subject, message, from_email, [self.email], **kwargs)