[Django]-AbstractUser vs AbstractBaseUser in Django?

167đź‘Ť

âś…

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.

52đź‘Ť

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.

30đź‘Ť

First of all, I explain AbstractUser then AbstractBaseUser. *You can see my answer explaining how to set up email and password authentication with AbstractUser or AbstractBaseUser and PermissionsMixin.

<AbstractUser>

AbstractUser class initially has 11 fields same as default User class(model) 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 to CustomUser(AbstractUser) class 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 CustomUser(AbstractUser) class 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 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 and 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 CustomUser(AbstractBaseUser) class as shown below. *Keep it in mind that 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 CustomUser(AbstractBaseUser) class 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 = []

4đź‘Ť

AbstractUser class is the subclass of AbstractBaseUser and PermissionsMixin classes and AbstractUser class has 11 fields same as default User class(model) 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

In addition, PermissionsMixin class is the superclass of AbstractUser and PermissionsMixin class has 2 fields as shown below:

id
is_superuser

*You can see my answer explaining how to set up email and password authentication with AbstractUser or AbstractBaseUser and PermissionsMixin.

0đź‘Ť

Main difference basically lies on Usecase. Say for example you no longer need the existing User class provided by Django and you only care about authentication functionalities provided by the User class and your own custom fields. In that case, you should use AbstractBaseUser. In another case, you want to use existing User Fields and functionalities but on top of that you would like to ad some extra fields and methods. In that case, you should use AbstractUser.

👤Rabeul Hasan

Leave a comment