Django:扩展AbstractBaseUser是否需要将电子邮件用作USERNAME_FIELD?

时间:2021-07-15 19:22:40

Like many others, I'm trying to configure my Django app to use the email as the username field. I have some existing user accounts that I migrated to a custom user model successfully, though right now the custom model is identical to the Django user model:

像其他许多人一样,我正在试图配置我的Django应用程序以使用电子邮件作为用户名字段。我已经成功地将一些现有的用户帐户迁移到自定义用户模型中,尽管现在自定义模型与Django用户模型相同:

# accounts/models.py

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
  pass

Anytime I try to set the USERNAME_FIELD to email, I get an error that the USERNAME_FIELD cannot be included in REQUIRED_FIELDS, as well as the error that it must be unique. (side note: email isn't required for the base User, so I don't understand the first error)

每当我试图将USERNAME_FIELD设置为电子邮件时,我都会遇到一个错误,即USERNAME_FIELD不能包含在REQUIRED_FIELDS中,也不能包含它必须惟一的错误。(附注:基本用户不需要电子邮件,所以我不理解第一个错误)

Is there any way to get this functionality while subclassing AbstractUser?

在子类化AbstractUser时,有什么方法可以获得这个功能吗?

Or do I just need to subclass AbstractBaseUser and specify every field, in the manner of this example?

或者我只需要子类AbstractBaseUser并以本例的方式指定每个字段?

If it's the latter, how would I go about making sure I define the exact same fields as the Django User model? I don't want to lose the users I have or cause issues with mismatched fields.

如果是后者,我该如何确保定义与Django用户模型完全相同的字段?我不想失去我拥有的用户,也不想因为不匹配字段而引起问题。

It seems silly to have to go all the way to fully specifying a custom model just to use an email address as the username, so maybe I'm missing something here. If there was a way to ensure unique fields in the Django User model, I don't think this would be an issue.

为了使用电子邮件地址作为用户名,必须完全指定自定义模型,这似乎很愚蠢,所以这里可能漏掉了一些东西。如果有一种在Django用户模型中确保惟一字段的方法,我不认为这是一个问题。

1 个解决方案

#1


1  

As Django doc says:

Django医生说:

If the changes you need are purely behavioral, and don’t require any change to what is stored in the database, you can create a proxy model based on User.

如果您需要的更改是纯行为的,并且不需要对数据库中存储的内容进行任何更改,那么您可以基于用户创建代理模型。

Since you want to substitute it with a custom User model:

因为您想用自定义用户模型替换它:

For instance, on some sites it makes more sense to use an email address as your identification token instead of a username.

例如,在某些网站上,使用电子邮件地址作为标识符而不是用户名更有意义。

You'll need to implement your own User model by subclassing AbstractBaseUser. Here is a sample code with django premissions included as well:

您需要通过子类化AbstractBaseUser来实现自己的用户模型。下面是django预任务的示例代码:

    class User(AbstractBaseUser, PermissionsMixin):
    """
    A class implementing a fully featured User model with admin-compliant
    permissions.

    Email and password are required. Other fields are optional.
    """

    email = models.EmailField(
        _('Email Address'), unique=True,
        error_messages={
            'unique': _("A user with that email already exists."),
        }
    )
    username = models.CharField(
        _('Username'), max_length=30, unique=True, blank=True, null=True,
        help_text=_('30 characters or fewer. Letters, digits and _ only.'),
        validators=[
            validators.RegexValidator(
                r'^\w+$',
                _('Enter a valid username. This value may contain only '
                  'letters, numbers and _ character.'),
                'invalid'
            ),
        ],
        error_messages={
            'unique': _("The username is already taken."),
        }
    )
    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()

    USERNAME_FIELD = 'email'

    class Meta(object):
        verbose_name = _('User')
        verbose_name_plural = _('Users')
        abstract = False

    def get_full_name(self):
        """
        Returns email instead of the fullname for the user.
        """
        return email_to_name(self.email)

    def get_short_name(self):
        """
        Returns the short name for the user.
        This function works the same as `get_full_name` method.
        It's just included for django built-in user comparability.
        """
        return self.get_full_name()

    def __str__(self):
        return self.email

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

#1


1  

As Django doc says:

Django医生说:

If the changes you need are purely behavioral, and don’t require any change to what is stored in the database, you can create a proxy model based on User.

如果您需要的更改是纯行为的,并且不需要对数据库中存储的内容进行任何更改,那么您可以基于用户创建代理模型。

Since you want to substitute it with a custom User model:

因为您想用自定义用户模型替换它:

For instance, on some sites it makes more sense to use an email address as your identification token instead of a username.

例如,在某些网站上,使用电子邮件地址作为标识符而不是用户名更有意义。

You'll need to implement your own User model by subclassing AbstractBaseUser. Here is a sample code with django premissions included as well:

您需要通过子类化AbstractBaseUser来实现自己的用户模型。下面是django预任务的示例代码:

    class User(AbstractBaseUser, PermissionsMixin):
    """
    A class implementing a fully featured User model with admin-compliant
    permissions.

    Email and password are required. Other fields are optional.
    """

    email = models.EmailField(
        _('Email Address'), unique=True,
        error_messages={
            'unique': _("A user with that email already exists."),
        }
    )
    username = models.CharField(
        _('Username'), max_length=30, unique=True, blank=True, null=True,
        help_text=_('30 characters or fewer. Letters, digits and _ only.'),
        validators=[
            validators.RegexValidator(
                r'^\w+$',
                _('Enter a valid username. This value may contain only '
                  'letters, numbers and _ character.'),
                'invalid'
            ),
        ],
        error_messages={
            'unique': _("The username is already taken."),
        }
    )
    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()

    USERNAME_FIELD = 'email'

    class Meta(object):
        verbose_name = _('User')
        verbose_name_plural = _('Users')
        abstract = False

    def get_full_name(self):
        """
        Returns email instead of the fullname for the user.
        """
        return email_to_name(self.email)

    def get_short_name(self):
        """
        Returns the short name for the user.
        This function works the same as `get_full_name` method.
        It's just included for django built-in user comparability.
        """
        return self.get_full_name()

    def __str__(self):
        return self.email

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)