Django
User Model
Django Extensions
Custom User Model
Django Development

What's the best way to extend the User model in Django?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The best way to extend Django's user model depends mostly on when you are making the decision. For a new project, the usual best practice is to create a custom user model from the start, typically by subclassing AbstractUser. For an existing project that already depends on the built-in user table, a one-to-one profile model is often safer.

Best Default for New Projects: Subclass AbstractUser

If you are starting a new Django project, create your own user model early and point AUTH_USER_MODEL at it. This gives you room to add fields later without being trapped by the default auth.User.

python
1from django.contrib.auth.models import AbstractUser
2from django.db import models
3
4
5class User(AbstractUser):
6    timezone = models.CharField(max_length=64, default="UTC")
7    marketing_opt_in = models.BooleanField(default=False)

Then in settings.py:

python
AUTH_USER_MODEL = "accounts.User"

This keeps the familiar Django username, password, groups, and permissions behavior while giving you a place for project-specific fields and methods.

Use AbstractBaseUser Only When You Truly Need It

Some projects need a completely custom authentication shape, such as email-only login with no username field or unusual admin behavior. In those cases, subclassing AbstractBaseUser gives full control, but it also requires more work because you must define fields, manager methods, and admin integration yourself.

That makes AbstractBaseUser powerful but not the best default. If all you need is "the standard user plus a few extra fields," AbstractUser is usually the right tradeoff.

Existing Project: Consider a Profile Model

If the project already uses the built-in user model in production, swapping to a custom user model late can be painful because of migrations, foreign keys, and third-party app assumptions. In that case, a one-to-one profile model is often the safer extension strategy.

python
1from django.conf import settings
2from django.db import models
3
4
5class UserProfile(models.Model):
6    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
7    timezone = models.CharField(max_length=64, default="UTC")
8    marketing_opt_in = models.BooleanField(default=False)

This does introduce an extra join, but it avoids a risky mid-project user model replacement.

Always Reference the User Model Indirectly

Whether you use a custom model or the built-in one, do not hardcode auth.User in app code. Use settings.AUTH_USER_MODEL for model relationships and get_user_model() when you need the class at runtime.

python
1from django.contrib.auth import get_user_model
2
3User = get_user_model()
4
5def create_demo_user():
6    return User.objects.create_user(
7        username="demo",
8        password="secret123"
9    )

This keeps reusable apps and internal code compatible with whichever user model the project uses.

Think About Authentication Requirements Early

The hardest part of user customization is not writing the model. It is deciding what the application means by "user." Questions worth answering early include:

  • Is email the real unique login field.
  • Do you need extra profile fields immediately.
  • Will third-party authentication providers be involved.
  • Do staff and customer accounts need different behavior.

Those decisions affect whether AbstractUser is enough or whether a deeper redesign is needed. Making them early is much cheaper than retrofitting later.

Avoid Mid-Project User Model Swaps

Changing AUTH_USER_MODEL after initial migrations is one of the more painful refactors in Django. It affects foreign keys, migration dependencies, admin setup, and sometimes third-party packages. If the project is still young, it can be worth doing. If the project is mature, a profile model or targeted custom tables are often safer.

Common Pitfalls

  • Starting a new project with the default user model and planning to change it later.
  • Choosing AbstractBaseUser when AbstractUser would have been simpler.
  • Referencing auth.User directly instead of AUTH_USER_MODEL or get_user_model().
  • Swapping the user model mid-project without understanding the migration cost.
  • Putting every unrelated domain field directly on the user model without clear need.

Summary

  • For new projects, a custom user model based on AbstractUser is usually the best default.
  • Use AbstractBaseUser only when you need full authentication customization.
  • For existing projects, a one-to-one profile model is often the safer extension path.
  • Always reference the user model indirectly with AUTH_USER_MODEL or get_user_model().
  • Decide user requirements early because changing the model later is expensive.

Course illustration
Course illustration

All Rights Reserved.