skip to content

Custom User Models

Replacing the default User means choosing AbstractUser or AbstractBaseUser with PermissionsMixin and setting AUTH_USER_MODEL early. Interviewers ask why switching later is so painful.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Django, when would you build a custom user model on AbstractUser rather than on AbstractBaseUser with PermissionsMixin?

level: middleimportance: must knowfreq 74%

answer

  1. how much of the default you keep
  2. username, names, staff flag already there
  3. only password and last_login
  4. permissions come from a mixin

basics

~20 s

Subclass AbstractUser to keep the full default user and add fields. Use AbstractBaseUser when the identity shape differs: it supplies only password and last_login, so you define the fields, USERNAME_FIELD and a manager, adding PermissionsMixin for groups.

solid answer

~40 s

`AbstractUser` is the complete default user as an abstract base: `username`, `first_name`, `last_name`, `email`, `is_staff`, `is_active`, `date_joined`, the password machinery and, through `PermissionsMixin`, `is_superuser`, `groups` and `user_permissions`. Subclass it when the default shape is right and you only add fields; it is the safe choice for a new project. `AbstractBaseUser` gives just `password`, `last_login` and the auth methods (`set_password`, `check_password`, `get_username`, `is_authenticated`). Choose it when you want to remove or redefine fields, for example no username at all. You then declare `USERNAME_FIELD` and `REQUIRED_FIELDS`, write a `BaseUserManager` subclass with `create_user` and `create_superuser`, add `PermissionsMixin` if you want Django's permission system, and define `is_staff` yourself if the admin must work.

code

python · 10 lines
python
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    display_name = models.CharField(max_length=80, blank=True)
    preferred_locale = models.CharField(max_length=10, default='en')

# settings.py
# AUTH_USER_MODEL = 'accounts.User'

go deeper

for a junior

Know that AbstractUser keeps the default fields and AbstractBaseUser keeps almost nothing, and that either one is activated by AUTH_USER_MODEL.

for a middle

List what AbstractBaseUser leaves you to write: USERNAME_FIELD, REQUIRED_FIELDS, a BaseUserManager with create_user and create_superuser, PermissionsMixin, and is_staff for the admin.

for a senior

Argue the choice from the identity model and the admin, and warn against the multi-table-inheritance trap of subclassing the concrete User.

for a principal

Weigh a fat user model against lean users with per-app related data: fewer joins versus apps that can evolve their user data without touching one shared table.

## Three layers in django.contrib.auth Django's user model is built from abstract classes stacked on each other, and a custom user model picks the layer it starts from: | Class | Where | Contributes | |---|---|---| | `AbstractBaseUser` | `django.contrib.auth.base_user` | fields `password` (max 128) and `last_login`; `is_active = True` as a **class attribute**; `set_password`, `check_password`, `get_username`, `natural_key`, `is_authenticated`, `is_anonymous`, session-hash helpers | | `PermissionsMixin` | `django.contrib.auth.models` | fields `is_superuser`, `groups`, `user_permissions`; methods `has_perm`, `has_perms`, `has_module_perms`, `get_all_permissions` | | `AbstractUser` | `django.contrib.auth.models` | inherits both, plus `username` (max 150, unique, validated), `first_name`, `last_name`, `email`, `is_staff`, `is_active` as a real field, `date_joined`, `objects = UserManager()` | | `User` | `django.contrib.auth.models` | a concrete `AbstractUser` subclass with `swappable = 'AUTH_USER_MODEL'` | So the **default User fields** are `id`, `username`, `password`, `first_name`, `last_name`, `email`, `is_staff`, `is_active`, `is_superuser`, `last_login`, `date_joined`, plus the `groups` and `user_permissions` many-to-many relations. ## When AbstractUser is the answer Subclass `AbstractUser` when the built-in shape is acceptable and you only need to **add** things: - extra columns such as a display name, a locale or a phone number; - extra methods or properties on the user; - a guarantee that you can change the model later, because the project already owns it. The Django docs show the minimal version, a `class User(AbstractUser): pass`, as the way to start a new project so that the model is yours from the first migration. Everything that expects the default user keeps working: `UserManager`, `createsuperuser`, the admin's `UserAdmin` and the permission checks. ## When AbstractBaseUser is the answer Start from `AbstractBaseUser` when the identity itself is different: - there is **no username**, and an email address, phone number or external identifier is the login key; - you want to **drop** fields such as `first_name`/`last_name` in favour of a single name field; - you want different types or constraints on core fields. The price is that you must supply what `AbstractUser` would have given you: 1. **`USERNAME_FIELD`** - the name of the unique identifying field. `AbstractBaseUser` defines no default. With the default `ModelBackend` the field must be unique, or the system check `auth.E003` fails. 2. **`REQUIRED_FIELDS`** - the fields `createsuperuser` prompts for. It defaults to an empty list and must not contain `USERNAME_FIELD` (`auth.E002`) or `password`. 3. **A manager** - a `BaseUserManager` subclass whose `create_user` and `create_superuser` accept the username field plus the required fields; `createsuperuser` calls `create_superuser(**user_data)` with exactly those keys. 4. **`is_active`** - optional; the class attribute already returns `True`, but a real `BooleanField` is what lets you deactivate accounts. 5. **`PermissionsMixin`** - optional but usual; without it there is no `is_superuser`, no groups and no per-user permissions. 6. **`is_staff`** - needed if the admin must accept the user, because the admin checks `is_staff` and `PermissionsMixin` does not define it. ## Why PermissionsMixin is separate `AbstractBaseUser` lives in `base_user.py` so that it can be imported without `django.contrib.auth` in `INSTALLED_APPS`. Groups and permissions, by contrast, are models of `django.contrib.auth`. Keeping them in a mixin lets a project that authenticates users without Django's permission tables build a lean user model, and lets everyone else opt in with one extra base class. ## What stays the same either way Whichever base you choose, a few things come from `AbstractBaseUser` and behave identically: - **Password handling** - `set_password()` hashes through `make_password()`, `check_password()` verifies and can upgrade the stored hash, and `set_unusable_password()` marks accounts that sign in only through an external provider. - **Identity helpers** - `get_username()` reads whatever field `USERNAME_FIELD` names, and `natural_key()` returns it, which is what fixtures and `get_by_natural_key()` rely on. - **Request-facing flags** - `is_authenticated` is always `True` and `is_anonymous` always `False` on a real user; both must stay properties, and a system check flags a model that turns them into methods. - **Normalisation** - `clean()` applies NFKC Unicode normalisation to the username field, so visually identical names collide as they should. This is why the choice is about **fields and managers**, not about security plumbing: the hashing and session-hash code is shared by both routes. ## A practical decision rule - New project, no special identity requirements: `AbstractUser`. - Login key or core fields differ from the default: `AbstractBaseUser` + `PermissionsMixin` + your own manager. - Only profile data differs, and several apps own parts of it: keep the user lean and put per-app data on related models. A common interview slip is thinking you can extend users by subclassing the concrete `User`. That creates multi-table inheritance, a second table joined one-to-one to `auth_user`, not a replacement user model.

  • Does a custom model on AbstractBaseUser work with the Django admin out of the box?
    Not automatically. The admin needs `is_active`, `is_staff`, `has_perm()` and `has_module_perms()`. `PermissionsMixin` supplies the two permission methods and `AbstractBaseUser` an always-true `is_active`, but `is_staff` must be defined by you. The admin forms also need adapting because `UserAdmin` assumes a `username` field.
  • What is wrong with extending users by subclassing django.contrib.auth.models.User?
    `User` is concrete, so a subclass is multi-table inheritance: a new table with a one-to-one link to `auth_user`. `AUTH_USER_MODEL` still names `auth.User`, authentication returns the parent class, and every access to the extra fields costs a join. Subclass the abstract `AbstractUser` instead.

AbstractUser is a furnished flat you can add shelves to; AbstractBaseUser is the bare shell with plumbing and locks installed, where you choose every piece of furniture yourself.

saying these in an interview costs you the question

  • AbstractBaseUser already includes username and email fields
  • PermissionsMixin adds is_staff, so the admin works automatically
  • Subclassing the concrete User class replaces the user model
  • AbstractUser is only for projects that keep the username login
  • USERNAME_FIELD defaults to username on AbstractBaseUser
open as a page

In Django, why should code reference settings.AUTH_USER_MODEL or get_user_model() instead of importing the User class directly?

level: juniorimportance: should knowfreq 58%

basics

~10 s

Django's user model is swappable: AUTH_USER_MODEL names whichever model the project uses. Importing auth.User hard-codes the default and breaks once it is swapped, so relations use the setting string and runtime code calls get_user_model().

open as a page

For a SaaS product where customers sign in by email, how do you define a Django custom user model with USERNAME_FIELD and BaseUserManager?

level: middleimportance: should knowfreq 52%

basics

~10 s

Subclass AbstractBaseUser and PermissionsMixin, declare a unique EmailField, set USERNAME_FIELD = 'email' and REQUIRED_FIELDS for other mandatory fields, and write a BaseUserManager subclass whose create_user and create_superuser take email instead of username.

open as a page

Why must a Django project set AUTH_USER_MODEL before its first migrate, and what does switching to a custom user model on a live database involve?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Every foreign key to the user and every migration that depends on it is recorded against the model active at the first migrate. Switching later needs a hand-built transition: new model on the old table, a manually recorded migration, and data and relation fixes.

open as a page