Difference between AbstractUser and AbstractBaseUser in Django?
First of all, I explain AbstractUser then AbstractBaseUser.
<AbstractUser>
"AbstractUser" class initially has 11 fields same as default "User" class as shown below and for the subclass of "AbstractUser" class, you can add new fields, change and remove initial fields. *Keep it in mind that "username" and "email" fields in the initial fields of "AbstractUser" class are special and only "username" field has Unique Constraint.
These are the initial fields of "AbstractUser" class which default "User" class has as shown below:
id
password
last_login
is_superuser
username (Special, Unique Constraint)
first_name
last_name
email (Special)
is_staff
is_active
date_joined
Now, set "pass" for the class "CustomUser(AbstractUser)" as shown below:
# "account/models.py"
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
pass
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, the initial fields of "AbstractUser" class are created in SQLite as shown below:
Next, set "age" and "gender" fields for the class "CustomUser(AbstractUser)" as shown below:
# "account/models.py"
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
age = models.IntegerField()
gender = models.CharField(max_length=100)
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, "age" and "gender" fields are created with the initial fields of "AbstractUser" class as shown below:
Next, change all the initial fields of "AbstractUser" class by setting "models.CharField(max_length=100)" to them but "id" field needs "primary_key=True" to have Primary Key otherwise there is an error and "username" field needs "unique=True" to have Unique Constraint otherwise there is a warning:
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)
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, all the initial fields of "AbstractUser" class are changed as shown below:
Next, remove "password", "last_login", "is_superuser" and "username" fields by setting "None" to them as shown below. *Keep it in mind that "id" field can never ever be removed even if setting "None" to it and USERNAME_FIELD must have one existing field and by default, "username" field which has Unique Constraint is set to USERNAME_FIELD so if removing "username" field by setting "None" to it, you also need to remove "username" field from USERNAME_FIELD by setting one existing field as shown below otherwise there is an error so in this example below, there are 7 existing fields "id", "first_name", "last_name", "email", "is_staff", "is_active" and "date_joined" so change USERNAME_FIELD from "username" field to "last_name" field by settiing "last_name" field with "unique=True" to "USERNAME_FIELD" as shown below. *Keep it in mind that like "last_name" field, the existing field set to "USERNAME_FIELD" needs "unique=True" to have Unique Constraint as shown below otherwise there is a warning but when setting "id" field which has Primary Key to "USERNAME_FIELD", it doesn't need "unique=True" to have Unique Constraint:
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
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, as shown below, "password", "last_login", "is_superuser" and "username" fields are removed and "last_name" field has Unique Constraint:
Next again, remove "password", "last_login", "is_superuser" and "username" fields by setting "None" to them as shown below but this time, change "USERNAME_FIELD" from "username" field to "email" field by settiing "email" field with "unique=True" to "USERNAME_FIELD" as shown below. *Keep it in mind that by default, "email" field is also set to "REQUIRED_FIELDS" and it's not allowed to set the same field to both "USERNAME_FIELD" and "REQUIRED_FIELDS" at the same time otherwise there is an error so to remove "email" field from "REQUIRED_FIELDS", set no fields to "REQUIRED_FIELDS" as shown below. *Keep it in mind that it's ok that no fields are set to "REQUIRED_FIELDS" as shown below:
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
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, as shown below, "password", "last_login", "is_superuser" and "username" fields are removed and "email" field has Unique Constraint:
This code below is the part of "AbstractUser" class in Django on Github. You can see the defined fields, USERNAME_FIELD = "username", REQUIRED_FIELDS = ["email"] and "AbstractUser" class is actually the subclass of "AbstractBaseUser" class which I'm going to explain next:
# "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" class initially has 3 fields as shown below and for the subclass of "AbstractBaseUser" class, you can add new fields, change and remove initial fields same as "AbstractUser" class.
These are the initial fields of "AbstractBaseUser" class as shown below:
id
password
last_login
Now, set "password" field with "unique=True" to "USERNAME_FIELD" in the class "CustomUser(AbstractBaseUser)" as shown below. *Keep it in mind that as you've already noticed, "AbstractBaseUser" class also has "USERNAME_FIELD" and by default, no field is set to "USERNAME_FIELD" so you need to set one existing field to it as shown below otherwise there is an error. In addition, no fields are set to "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'
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, the initial fields of "AbstractBaseUser" class are created in SQLite as shown below:
Next, set "age" and "gender" fields for the class "CustomUser(AbstractBaseUser)" and set "age" field with "unique=True" to "USERNAME_FIELD" as shown below:
# "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'
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, "age" and "gender" fields are created with the initial fields of "AbstractBaseUser" class and Unique Constraint is set to "age" field as shown below:
Next, change all the initial fields of "AbstractBaseUser" class by setting "models.CharField(max_length=100)" to them and set "password" field with "unique=True" to "USERNAME_FIELD" and same as "AbstractUser" class, "id" field needs "primary_key=True" to have Primary Key otherwise there is an error:
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'
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, all the initial fields of "AbstractBaseUser" class are changed and Unique Constraint is set to "password" field as shown below:
Next, remove "password" and "last_login" fields by setting "None" to them and set only one existing field "id" to "USERNAME_FIELD" as shown below. *Keep it in mind that same as "AbstractUser", "id" field can never ever be removed even if setting "None" to it and when setting "id" field which has Primary Key to "USERNAME_FIELD", it doesn't need "unique=True" to have Unique Constraint:
from django.contrib.auth.models import AbstractBaseUser
class CustomUser(AbstractBaseUser):
password = None
last_login = None
USERNAME_FIELD = 'id'
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Then, as shown below, "password" and "last_login" fields are removed:
This code below is the part of "AbstractBaseUser" class in Django on Github. You can see the defined fields, "USERNAME_FIELD" is not defined and 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 = []
"AbstractUser" class is the subclass of "AbstractBaseUser" class and "AbstractUser" class has 11 fields same as default "User" class as shown below:
id
password
last_login
is_superuser
username
first_name
last_name
email
is_staff
is_active
date_joined
"AbstractBaseUser" class is the superclass of "AbstractUser" class and "AbstractBaseUser" class has 3 fields as shown below:
id
password
last_login
The AbstractUser is basically just the "User" class you're probably already used to. AbstractBaseUser makes fewer assumptions and you have to tell it what field represents the username, what fields are required, and how to manage those users.
If you're just adding things to the existing user (i.e. profile data with extra fields), then use AbstractUser because it's simpler and easier. If you want to rethink some of Django's assumptions about authentication, then AbstractBaseUser gives you the power to do so.
The documentation explains this fully. AbstractUser
is a full User model, complete with fields, as an abstract class so that you can inherit from it and add your own profile fields and methods. AbstractBaseUser
only contains the authentication functionality, but no actual fields: you have to supply them when you subclass.