Django cookbook · Beginner

Start a project with a custom user model

Use email as the login field and keep room to add profile fields later without a painful migration.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Define a custom user model before the first migration, even if it only subclasses AbstractUser. Point AUTH_USER_MODEL at it and always refer to users through get_user_model() or settings.AUTH_USER_MODEL.

Solution

# accounts/models.py
from django.contrib.auth.models import AbstractUser, UserManager
from django.db import models


class EmailUserManager(UserManager):
    def create_user(self, email, password=None, **extra):
        return super().create_user(username=email, email=email, password=password, **extra)

    def create_superuser(self, email, password=None, **extra):
        return super().create_superuser(username=email, email=email, password=password, **extra)


class User(AbstractUser):
    email = models.EmailField(unique=True)
    display_name = models.CharField(max_length=100, blank=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    objects = EmailUserManager()


# settings.py
AUTH_USER_MODEL = "accounts.User"


# orders/models.py
from django.conf import settings


class Order(models.Model):
    customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="orders")

Pitfalls

  • Switching to a custom user model after the first migration; it requires rewriting migrations and data.
  • Importing django.contrib.auth.models.User directly in other apps.
  • Case-sensitive email uniqueness; normalise emails (lowercase) on save or use a functional unique constraint.

Variations

  • Keep profile data in a separate one-to-one Profile model if it's large or owned by another app.
  • Add a UniqueConstraint(Lower("email"), name="unique_email_ci") for case-insensitive uniqueness.

Further reading: Substituting a custom user model

Reference: https://docs.djangoproject.com/en/6.0/topics/auth/customizing/#substituting-a-custom-user-model

More Django recipes

Was this page helpful?