Difference between AbstractUser and AbstractBaseUser in Django?

PythonDjango

Python Problem Overview


The use of AbstractUser and AbstractBaseUser looks quite similar.

from django.contrib.auth.models import AbstractUser, AbstractBaseUser

What is the difference between the two?

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

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.

Solution 3 - Python

First of all, I explain AbstractUser then AbstractBaseUser.

<AbstractUser>

"AbstractUser" class initially has the same 11 fields which default "User" class has 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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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:

enter image description here

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 = []

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPranjal MittalView Question on Stackoverflow
Solution 1 - PythonDaniel RosemanView Answer on Stackoverflow
Solution 2 - PythonPiyush S. WanareView Answer on Stackoverflow
Solution 3 - PythonKai - Kazuya ItoView Answer on Stackoverflow