ビルトインUserモデルを修正変更したい場合
ベース記事_Userモデル
内容一覧
ビルトインのユーザーモデルの呼び出し方
from django.contrib.auth.models import User
ビルトインのUserモデルを修正変更したい場合
既存のUserモデルを変更したい場面に出くわした。自分の場合はmodels.pyにあるフィールドにUserモデルを外部キーとして利用する。その際にon_delete.models.SET_NULLと設定したが、エラーが出てしまう。これはUserモデルのuserフィールドでnull=Trueと設定されていることが条件である。しかしながらUserモデルをnull=Trueに修正変更することは無理?っぽい。 nullをセットしたい要望がある場合には、カスタムユーザーモデルを準備しなければならないことがわかった。 カスタムユーザーモデルとは、djangoで提供しているUserモデルやその関連するソースコードをもとに継承したモデルのことである。
実際にUserモデルのソースコードを確認する
探し方について
djangoのビルトインUSERモデルのソースコードは以下にある。
https://github.com/django/django/blob/master/django/contrib/auth/models.py
Userモデルは、from django.contrib.auth.models import Userと呼び出すので、githubでは、django,contrib,auth,modelsの順番でディレクトリ、ファイル移動すればUserモデルのソースコードを発見できる。
Userは、AbstractUserを継承したものである。またAbstractUserはAbstractBaseUserとPermissionsMixinを複合で継承したものである。Userを理解するためにはこれらのものがどのように記述されているか確認することが必要である。以下にコードを置く。
User
https://github.com/django/django/blob/master/django/contrib/auth/models.py
class User(AbstractUser): """ Users within the Django authentication system are represented by this model. Username and password are required. Other fields are optional. """ class Meta(AbstractUser.Meta): swappable = 'AUTH_USER_MODEL'
AbstractUser
https://github.com/django/django/blob/master/django/contrib/auth/models.py
class AbstractUser(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. Username and password are required. Other fields are optional. """ username_validator = UnicodeUsernameValidator() username = models.CharField( _('username'), max_length=150, unique=True, help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ 'unique': _("A user with that username already exists."), }, ) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=True) email = models.EmailField(_('email address'), blank=True) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), ) is_active = models.BooleanField( _('active'), default=True, help_text=_( 'Designates whether this user should be treated as active. ' 'Unselect this instead of deleting accounts.' ), ) date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() EMAIL_FIELD = 'email' USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] class Meta: verbose_name = _('user') verbose_name_plural = _('users') abstract = True def clean(self): super().clean() self.email = self.__class__.objects.normalize_email(self.email) def get_full_name(self): """ Return the first_name plus the last_name, with a space in between. """ full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): """Return the short name for the user.""" return self.first_name def email_user(self, subject, message, from_email=None, **kwargs): """Send an email to this user.""" send_mail(subject, message, from_email, [self.email], **kwargs)
PermissionsMixin
https://github.com/django/django/blob/master/django/contrib/auth/models.py
class PermissionsMixin(models.Model): """ Add the fields and methods necessary to support the Group and Permission models using the ModelBackend. """ is_superuser = models.BooleanField( _('superuser status'), default=False, help_text=_( 'Designates that this user has all permissions without ' 'explicitly assigning them.' ), ) groups = models.ManyToManyField( Group, verbose_name=_('groups'), blank=True, help_text=_( 'The groups this user belongs to. A user will get all permissions ' 'granted to each of their groups.' ), related_name="user_set", related_query_name="user", ) user_permissions = models.ManyToManyField( Permission, verbose_name=_('user permissions'), blank=True, help_text=_('Specific permissions for this user.'), related_name="user_set", related_query_name="user", ) class Meta: abstract = True def get_group_permissions(self, obj=None): """ Return a list of permission strings that this user has through their groups. Query all available auth backends. If an object is passed in, return only permissions matching this object. """ permissions = set() for backend in auth.get_backends(): if hasattr(backend, "get_group_permissions"): permissions.update(backend.get_group_permissions(self, obj)) return permissions def get_all_permissions(self, obj=None): return _user_get_all_permissions(self, obj) def has_perm(self, perm, obj=None): """ Return True if the user has the specified permission. Query all available auth backends, but return immediately if any backend returns True. Thus, a user who has permission from a single auth backend is assumed to have permission in general. If an object is provided, check permissions for that object. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True # Otherwise we need to check the backends. return _user_has_perm(self, perm, obj) def has_perms(self, perm_list, obj=None): """ Return True if the user has each of the specified permissions. If object is passed, check if the user has all required perms for it. """ return all(self.has_perm(perm, obj) for perm in perm_list) def has_module_perms(self, app_label): """ Return True if the user has any permissions in the given app label. Use similar logic as has_perm(), above. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True return _user_has_module_perms(self, app_label)
AbstractBaseUser
https://github.com/django/django/blob/master/django/contrib/auth/base_user.py
class AbstractBaseUser(models.Model): password = models.CharField(_('password'), max_length=128) last_login = models.DateTimeField(_('last login'), blank=True, null=True) is_active = True REQUIRED_FIELDS = [] # Stores the raw password if set_password() is called so that it can # be passed to password_changed() after the model is saved. _password = None class Meta: abstract = True def __str__(self): return self.get_username() def save(self, *args, **kwargs): super().save(*args, **kwargs) if self._password is not None: password_validation.password_changed(self._password, self) self._password = None def get_username(self): """Return the username for this User.""" return getattr(self, self.USERNAME_FIELD) def clean(self): setattr(self, self.USERNAME_FIELD, self.normalize_username(self.get_username())) def natural_key(self): return (self.get_username(),) @property def is_anonymous(self): """ Always return False. This is a way of comparing User objects to anonymous users. """ return False @property def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, raw_password): self.password = make_password(raw_password) self._password = raw_password def check_password(self, raw_password): """ Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) # Password hash upgrades shouldn't be considered password changes. self._password = None self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Set a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): """ Return False if set_unusable_password() has been called for this user. """ return is_password_usable(self.password) def get_session_auth_hash(self): """ Return an HMAC of the password field. """ key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" return salted_hmac(key_salt, self.password).hexdigest() @classmethod def get_email_field_name(cls): try: return cls.EMAIL_FIELD except AttributeError: return 'email' @classmethod def normalize_username(cls, username): return unicodedata.normalize('NFKC', username) if isinstance(username, str) else username