Creating a Custom User Model in Django

Introduction

When developing a Django application, user authentication is one of the most essential parts. Django provides a robust and secure authentication system out of the box, which includes a default User model. This built-in model includes fields like username, first_name, last_name, email, password, and a few others that cover the most common requirements.

However, as your project grows, you might find that you need to store additional information about your users. For example, you might want to add a phone number, date of birth, profile image, address, or even link users to other models with custom logic.

In such cases, relying solely on Django’s default User model becomes limiting. Fortunately, Django provides a powerful and flexible way to create a custom user model that extends or replaces the default one. This approach ensures your project remains scalable and maintainable.

In this post, we will go step-by-step through the process of creating a custom user model in Django using AbstractUser. We will discuss best practices, configuration steps, and common pitfalls you should avoid.

Why Create a Custom User Model?

Before we jump into implementation, it’s important to understand why we might want to create a custom user model.

1. To Add Extra Fields

The default Django user model doesn’t include fields like phone number, date of birth, or profile picture. If your application requires any of these, you’ll need to extend the model.

For example:

  • A social media platform might need a profile image.
  • An e-commerce app might require shipping addresses and contact numbers.
  • A business management system might need job titles and departments.

2. To Use Email Instead of Username for Login

By default, Django uses the username field for authentication. However, many modern applications prefer logging in with an email address. A custom user model lets you redefine authentication fields easily.

3. To Maintain Consistency Across Projects

If you’re building large-scale or multi-tenant systems, defining a base user model gives you flexibility and consistency across your projects.

4. To Avoid Future Refactoring

Once a project grows, changing the user model becomes complicated. Django recommends defining a custom user model at the beginning of your project, even if you only add a few fields later.


Django’s Built-in Options for User Models

Django offers three main approaches to work with user models:

1. Using the Default User Model

You can use Django’s built-in User model from django.contrib.auth.models. This is fine for simple apps that only need standard fields.

Example:

from django.contrib.auth.models import User

2. Extending the User Model with a One-to-One Link

If you already have an existing user model but want to add more fields without altering it, you can create a new model linked via a one-to-one relationship.

Example:

from django.contrib.auth.models import User
from django.db import models

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=15, blank=True)
image = models.ImageField(upload_to='profiles/', blank=True)

This approach works well for small extensions but can feel cumbersome if you need to access these fields frequently.

3. Creating a Custom User Model

The best long-term solution is to create your own user model by extending AbstractUser or AbstractBaseUser.

This gives you complete control over fields, authentication methods, and relationships.

In this tutorial, we’ll focus on the AbstractUser approach because it’s the simplest and most beginner-friendly.


Step 1: Create a New Django App

If you haven’t already created a Django app for user management, create one first. It’s common practice to have a dedicated app named accounts or users.

In your terminal:

python manage.py startapp accounts

Then add the new app to your settings.py file:

INSTALLED_APPS = [
...,
'accounts',
]

This ensures Django recognizes the app and can run its models during migrations.


Step 2: Create the Custom User Model

Inside your accounts/models.py file, import AbstractUser and create your own model by extending it.

Here’s a simple example:

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
phone = models.CharField(max_length=15, blank=True)

Explanation

  • AbstractUser: This base class already includes all fields and methods from Django’s default user model (like username, email, password, groups, etc.).
  • phone: We’ve added an additional field named phone, which is optional (blank=True) and limited to 15 characters.

You can add as many custom fields as your project requires, such as:

profile_image = models.ImageField(upload_to='profiles/', blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
address = models.TextField(blank=True)

Step 3: Update the Settings File

Now we need to tell Django that we’re using this new user model instead of the default one.

Open settings.py and add this line:

AUTH_USER_MODEL = 'accounts.CustomUser'

Here, 'accounts' refers to your app name, and 'CustomUser' is the model name.

This line must be added before running the first migration in your project. Once you’ve run migrations, changing the user model becomes very difficult.


Step 4: Create and Apply Migrations

Next, create and apply migrations for the new user model.

In your terminal:

python manage.py makemigrations
python manage.py migrate

Django will create the required database tables for your custom user model.


Step 5: Use the Custom User Model Throughout the Project

When referencing the user model anywhere else in your project, never import the User model directly from django.contrib.auth.models. Instead, always use the get_user_model() function or reference the model using settings.

Example 1: Using get_user_model()

from django.contrib.auth import get_user_model

User = get_user_model()

Now, you can safely use User.objects.create_user(...) or User.objects.filter(...).

Example 2: Using the Settings Reference

You can also import it directly using:

from django.conf import settings

user = settings.AUTH_USER_MODEL

This approach ensures that even if your user model changes later, your code remains compatible.


Step 6: Creating a Custom User Form (Optional)

If you’re using Django’s built-in authentication forms (such as for signup or admin), you may need to customize them to handle the new fields.

Example form in accounts/forms.py:

from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
    model = CustomUser
    fields = ('username', 'email', 'phone')
class CustomUserChangeForm(UserChangeForm):
class Meta:
    model = CustomUser
    fields = ('username', 'email', 'phone')

Step 7: Register the Custom User in Admin

By default, Django’s admin site won’t automatically recognize your custom user model. You need to register it properly using a custom admin class.

In accounts/admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
from .forms import CustomUserCreationForm, CustomUserChangeForm

class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['username', 'email', 'phone', 'is_staff']
admin.site.register(CustomUser, CustomUserAdmin)

Explanation

  • add_form: Used when creating a new user in the admin panel.
  • form: Used when editing an existing user.
  • list_display: Specifies which fields appear in the user list view in the admin.

Once this is done, run the development server and log in to the admin interface. You should see the phone field (and any others you added) available in the user model.


Step 8: Creating and Managing Users

You can now create new users from:

  1. The Django Admin Interface, or
  2. Through the Django shell.

Example in shell:

python manage.py shell

Then run:

from accounts.models import CustomUser

user = CustomUser.objects.create_user(
username='john_doe',
email='[email protected]',
password='securepassword123',
phone='1234567890'
) print(user)

This creates a user with your custom fields.


Step 9: Authenticating Users

Authentication works the same way as with the default model.

You can use Django’s authentication system like this:

from django.contrib.auth import authenticate

user = authenticate(username='john_doe', password='securepassword123')
if user is not None:
print("User authenticated successfully")
else:
print("Invalid credentials")

Step 10: Working with Email as the Username (Optional)

If you want users to log in using their email instead of a username, modify your CustomUser model like this:

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
username = None
email = models.EmailField(unique=True)
phone = models.CharField(max_length=15, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []

Now Django will use email for authentication.
Don’t forget to update your forms and admin classes accordingly.


Step 11: Testing Your Custom User Model

Testing is essential to ensure everything works correctly.

Here’s an example unit test in accounts/tests.py:

from django.test import TestCase
from django.contrib.auth import get_user_model

class CustomUserTests(TestCase):

def test_create_user(self):
    User = get_user_model()
    user = User.objects.create_user(
        username='john',
        email='[email protected]',
        password='securepassword123',
        phone='9998887777'
    )
    self.assertEqual(user.username, 'john')
    self.assertEqual(user.email, '[email protected]')
    self.assertTrue(user.check_password('securepassword123'))
def test_create_superuser(self):
    User = get_user_model()
    admin_user = User.objects.create_superuser(
        username='admin',
        email='[email protected]',
        password='adminpass'
    )
    self.assertTrue(admin_user.is_superuser)
    self.assertTrue(admin_user.is_staff)

Run tests with:

python manage.py test

Common Mistakes to Avoid

Creating a custom user model is simple, but a few mistakes can cause major issues later.

1. Changing the User Model After Migrations

Never change your user model after running your first migration. Doing so requires complex database refactoring.

2. Forgetting to Update AUTH_USER_MODEL

If you forget to set AUTH_USER_MODEL in settings.py, Django will still use the default model, causing conflicts later.

3. Directly Importing User

Always use get_user_model() instead of importing the default User.

4. Not Registering in Admin

If you forget to register your custom model in the admin, you won’t be able to manage users easily.


Step 12: Using Custom User in Views and Templates

When working with views or templates, your custom fields are available like any other Django model field.

Example View

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def profile_view(request):
return render(request, 'accounts/profile.html', {'user': request.user})

Example Template (profile.html)

<h2>Welcome, {{ user.username }}</h2>
<p>Email: {{ user.email }}</p>
<p>Phone: {{ user.phone }}</p>

Step 13: Extending the Model Further

You can further enhance your user model by adding related models or utility methods.

Example: Adding a Profile Method

class CustomUser(AbstractUser):
phone = models.CharField(max_length=15, blank=True)
def get_full_contact_info(self):
    return f"{self.username} - {self.phone}"

Now you can use:

user.get_full_contact_info()

to quickly display combined details.


Step 14: Integrating with Django REST Framework (Optional)

If your project uses Django REST Framework (DRF), you can easily serialize your custom user model.

Example Serializer

from rest_framework import serializers
from .models import CustomUser

class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
    model = CustomUser
    fields = ('id', 'username', 'email', 'phone')

Example ViewSet

from rest_framework import viewsets
from .models import CustomUser
from .serializers import CustomUserSerializer

class CustomUserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *