在Django中,AbstractUser和AbstractBaseUser有什么区别?

97
使用AbstractUserAbstractBaseUser看起来非常相似。
from django.contrib.auth.models import AbstractUser, AbstractBaseUser

两者之间有什么区别?
5个回答

167

文档对此作了详细解释。 AbstractUser 是完整的用户模型,包含字段,作为抽象类供您继承并添加自己的配置文件字段和方法。而 AbstractBaseUser 仅包含认证功能,但没有实际字段:当你创建子类时,必须提供它们。


52

AbstractUser基本上就是你可能已经习惯了的"用户(User)"类。AbstractBaseUser做出的假设更少,您需要告诉它哪个字段代表用户名,哪些字段是必需的,以及如何管理这些用户。

如果您只是在现有用户中添加一些内容(例如具有额外字段的配置文件数据),则使用AbstractUser因为它更简单易用。如果您想重新考虑Django关于认证的某些假设,那么AbstractBaseUser能够赋予您这样做的能力。


18
你可以告诉AbstractUser类使用哪个字段作为用户名。 - Raunaqss

30
首先,我解释一下AbstractUser然后是AbstractBaseUser。*你可以看到我的回答,解释了如何使用emailpassword验证来设置AbstractUserAbstractBaseUser以及PermissionsMixin

<AbstractUser>

AbstractUser类最初有11个字段,与默认的User类(模型)相同,如下所示。对于AbstractUser类的子类,您可以添加新字段,更改和删除初始字段。请记住,AbstractUser类中的usernameemail字段是特殊的,只有username字段具有唯一约束

这些是AbstractUser类的初始字段,与默认的User类相同,如下所示:

id
password
last_login
is_superuser
username (Special, Unique Constraint)
first_name
last_name
email (Special)
is_staff
is_active
date_joined

现在,按照下面的示例,将pass设置为CustomUser(AbstractUser)类:

# "account/models.py"

from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    pass

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,AbstractUser类的初始字段在SQLite中创建如下所示:

enter image description here

接下来,按照下面的示例,为CustomUser(AbstractUser)类设置agegender字段:
# "account/models.py"

from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    age = models.IntegerField()
    gender = models.CharField(max_length=100)

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,使用以下代码创建了具有AbstractUser类的初始字段的agegender字段:

enter image description here

接下来,通过将AbstractUser类的所有初始字段设置为models.CharField(max_length=100),来进行更改,但是id字段需要primary_key=True来拥有主键,否则会出现错误,而username字段需要unique=True来拥有唯一约束,否则会出现警告:
from django.db import models
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):           # ↓ Here ↓
    id = models.CharField(max_length=100, primary_key=True)
    password = models.CharField(max_length=100)
    last_login = models.CharField(max_length=100)
    is_superuser = models.CharField(max_length=100) # ↓ Here
    username = models.CharField(max_length=100, unique=True)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    is_staff = models.CharField(max_length=100)
    is_active = models.CharField(max_length=100)
    date_joined = models.CharField(max_length=100)

然后,运行下面的命令:
python manage.py makemigrations && python manage.py migrate

然后,AbstractUser类的所有初始字段如下所示进行了更改:

enter image description here

接下来,通过将以下字段设置为None来移除passwordlast_loginis_superuserusername字段。*请记住,即使将id字段设置为None,也不能删除它,并且USERNAME_FIELD必须有一个现有的字段,默认情况下,username字段被设置为USERNAME_FIELD,该字段具有唯一约束条件,因此如果通过将其设置为None来删除username字段,则还需要通过将一个现有字段设置为USERNAME_FIELD来从USERNAME_FIELD中删除username字段,如下所示,否则会出错,因此在下面的示例中,存在7个现有字段idfirst_namelast_nameemailis_staffis_activedate_joined,因此将USERNAME_FIELDusername字段更改为last_name字段,通过将last_name字段设置为unique=True,将last_name字段设置为USERNAME_FIELD,如下所示。*请记住,像last_name字段一样,设置为USERNAME_FIELD的现有字段需要unique=True以具有唯一约束条件,如下所示,否则会出现警告,但是当将具有主键的id字段设置为USERNAME_FIELD时,它不需要unique=True以具有唯一约束条件。
from django.db import models
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    password = None
    last_login = None
    is_superuser = None
    username = None                              # Here
    last_name = models.CharField(max_length=150, unique=True)
    
    USERNAME_FIELD = 'last_name' # Here

然后,运行下面的命令:
python manage.py makemigrations && python manage.py migrate

然后,如下所示,passwordlast_loginis_superuserusername字段被删除,而last_name字段具有唯一约束

enter image description here

下一步,再次移除passwordlast_loginis_superuserusername字段,将它们设置为None,如下所示。但是这次,将USERNAME_FIELDusername字段更改为email字段,通过将email字段设置为unique=True,将其设置为USERNAME_FIELD,如下所示。请记住,默认情况下,email字段也被设置为REQUIRED_FIELDS,不允许同时将同一个字段设置为USERNAME_FIELDREQUIRED_FIELDS,否则会出错,因此请将REQUIRED_FIELDS设置为no fields,如下所示。请记住,将REQUIRED_FIELDS设置为no fields也是可以的,如下所示:
from django.db import models
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    password = None
    last_login = None
    is_superuser = None
    username = None           # Here               
    email = models.EmailField(unique=True)

    USERNAME_FIELD = 'email' # Here
    REQUIRED_FIELDS = [] # Here

然后,运行下面的命令:
python manage.py makemigrations && python manage.py migrate

然后,如下所示,passwordlast_loginis_superuserusername字段被移除,而email字段则具有唯一约束

enter image description here

下面的代码是 Django 在 Github 上的 AbstractUser 类的部分。您可以看到定义的字段,USERNAME_FIELD = "username"REQUIRED_FIELDS = ["email"],并且 AbstractUser 类实际上是 AbstractBaseUser 类的子类,我接下来会解释:

# "django/contrib/auth/models.py"

class AbstractUser(AbstractBaseUser, PermissionsMixin):

    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=150, 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"]

<AbstractBaseUser>

AbstractBaseUser类最初有以下3个字段,对于AbstractBaseUser类的子类,您可以添加新字段,并像AbstractUser类一样更改和删除初始字段。

以下是AbstractBaseUser类的初始字段:

id
password
last_login

现在,在CustomUser(AbstractBaseUser)类中,将password字段设置为unique=TrueUSERNAME_FIELD,如下所示。请记住,AbstractBaseUser类也有USERNAME_FIELD,默认情况下没有字段设置为USERNAME_FIELD,因此您需要按照下面所示设置一个现有字段,否则会出错。此外,REQUIRED_FIELDS也没有设置任何字段。
# "account/models.py"

from django.db import models
from django.contrib.auth.models import AbstractBaseUser

class CustomUser(AbstractBaseUser):        # ↓ Here ↓ 
    password = models.CharField(max_length=128, unique=True)
    # ↓ Here ↓
    USERNAME_FIELD = 'password'

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,AbstractBaseUser类的初始字段在SQLite中创建如下所示:

enter image description here

接下来,为CustomUser(AbstractBaseUser)类设置agegender字段,并将age字段设置为unique=True,如下所示:将age字段设置为USERNAME_FIELD
# "account/models.py"

from django.db import models
from django.contrib.auth.models import AbstractBaseUser

class CustomUser(AbstractBaseUser):
    age = models.IntegerField(unique=True)
    gender = models.CharField(max_length=100)

    USERNAME_FIELD = 'age'

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,使用AbstractBaseUser类的初始字段创建agegender字段,并将age字段设置为唯一约束,如下所示:

enter image description here

接下来,通过将AbstractBaseUser类的所有初始字段设置为models.CharField(max_length=100),并将password字段设置为unique=True,将USERNAME_FIELD设置为与AbstractUser类相同。此外,id字段需要设置primary_key=True以拥有主键,否则会出现错误。
from django.db import models
from django.contrib.auth.models import AbstractBaseUser

class CustomUser(AbstractBaseUser):       # ↓ Here ↓
    id = models.CharField(max_length=100, primary_key=True)
    password = models.CharField(max_length=100, unique=True)
    last_login = models.CharField(max_length=100)
    
    USERNAME_FIELD = 'password'

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,AbstractBaseUser类的所有初始字段都被更改,并且password字段被设置为唯一约束,如下所示:

enter image description here

接下来,通过将passwordlast_login字段设置为None来移除它们,并且只将一个现有字段id设置为USERNAME_FIELD,如下所示。请记住,与AbstractUser一样,id字段永远不能被移除,即使将其设置为None,并且当将具有主键id字段设置为USERNAME_FIELD时,不需要unique=True来拥有唯一约束
from django.contrib.auth.models import AbstractBaseUser

class CustomUser(AbstractBaseUser):
    password = None
    last_login = None
        
    USERNAME_FIELD = 'id'

然后,运行以下命令:
python manage.py makemigrations && python manage.py migrate

然后,如下所示,passwordlast_login字段被移除:

enter image description here

以下代码是Django在Github上的AbstractBaseUser类的一部分。你可以看到定义的字段,USERNAME_FIELD没有被定义,而REQUIRED_FIELDS = []
# "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 = []

2
信息丰富、详细和清晰的解释。 - rs_punia
2
最终......找到了一些清晰详细且准确的解释...... - usamayaseen
2
这是一个非常好的解释。看起来我可以使用AbstractUser进行任何自定义操作。那么AbstractBaseUser有什么用呢?请给我们一些启示。 - Imtiaz Ahmed

4

AbstractUser类是AbstractBaseUserPermissionsMixin类的子类,AbstractUser类与默认的User类(模型)具有相同的11个字段,如下所示:

id
password
last_login
is_superuser
username
first_name
last_name
email
is_staff
is_active
date_joined
AbstractBaseUser类是AbstractUser类的超类,而AbstractBaseUser类有以下3个字段:
id
password
last_login

此外,PermissionsMixin 类是 AbstractUser 的超类,并且 PermissionsMixin 类具有如下所示的2个字段:
id
is_superuser

您可以查看我的答案,其中解释了如何使用AbstractUserAbstractBaseUserPermissionsMixin设置电子邮件和密码验证。


0

主要区别基本上在于用例。例如,假设您不再需要Django提供的现有User类,并且您只关心User类提供的身份验证功能和自己的自定义字段。在这种情况下,您应该使用AbstractBaseUser。在另一种情况下,您想要使用现有的User字段和功能,但是在此基础上还想添加一些额外的字段和方法。在这种情况下,您应该使用AbstractUser。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接