Day 4: Implementing a Custom User Model in Django and Creating a Superuser

Welcome back to our Django tutorial series! In Day 4, we'll delve into creating a custom user model in Django and how to register it. We'll also walk through creating a superuser to test our new model. By the end of this tutorial, you'll have a custom user model that fits your project's specific needs.

Why Create a Custom User Model?

While Django provides a default User model, it might not cover all the fields your project requires. If you need to store additional user information—such as a profile picture, address, or job title—it's better to create a custom user model. You can achieve this by creating an accounts app and defining a CustomUser model with the extra fields you need.

Registering the Accounts App

In the previous article, we created an app called accounts. Before we start modifying models, we need to register this app in our project's settings.py file.

Open projectpulse/settings.py and add "accounts" to the INSTALLED_APPS list:

# projectpulse/settings.py

INSTALLED_APPS = [
    "accounts",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    # ... other installed apps
]

This tells Django to include the accounts app when running migrations and other management commands.

Creating the Custom User Model

Now, let's create a custom user model step by step. We'll break it down into three main parts:

  1. Defining User Roles using Enums

  2. Creating a Custom User Manager

  3. Defining the Custom User Model

All of these will be added to accounts/models.py.

Note: Make sure to import necessary modules as we proceed.

1. Defining User Roles with Enums

First, we'll define user roles using Python's enum module. This allows us to have a set of predefined choices for user roles.

# accounts/models.py

from enum import Enum

class UserRoles(Enum):
    """Defines the user roles within the application."""
    ADMIN = "Admin"
    PROJECT_MANAGER = "Project Manager"
    TEAM_MEMBER = "Team Member"

2. Creating a Custom User Manager

Next, we'll create a custom user manager by extending BaseUserManager. This manager will handle the creation of regular users and superusers.

# accounts/models.py

from django.contrib.auth.base_user import BaseUserManager

class CustomUserManager(BaseUserManager):
    """Custom user manager for the CustomUser model."""

    def create_user(self, email, password=None, **extra_fields):
        """Creates and returns a regular user."""
        if not email:
            raise ValueError("The Email field must be set.")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        """Creates and returns a superuser."""
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("role", UserRoles.ADMIN.value)

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")
        if extra_fields.get("role") != UserRoles.ADMIN.value:
            raise ValueError("Superuser must have role=Admin.")

        return self.create_user(email, password, **extra_fields)

Explanation:

  • create_user: Handles the creation of regular users. It normalizes the email, sets the password, and saves the user.

  • create_superuser: Handles the creation of superusers. It ensures that certain fields are set appropriately.

3. Defining the Custom User Model

Now, we'll define the CustomUser model by extending AbstractUser. We'll replace the username field with an email field and add additional fields as needed.

# accounts/models.py

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _

class CustomUser(AbstractUser):
    """
    Custom user model that uses email instead of username.
    Additional fields can be added as required.
    """
    username = None
    email = models.EmailField(_("email address"), unique=True)
    profile_picture = models.ImageField(upload_to="profile_pictures/", blank=True, null=True)
    role = models.CharField(
        max_length=20,
        choices=[(role.value, role.value) for role in UserRoles],
        default=UserRoles.TEAM_MEMBER.value
    )

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["first_name", "last_name"]

    objects = CustomUserManager()

    def __str__(self):
        return f"{self.id} - {self.get_full_name()}"

Explanation:

  • username = None: We remove the username field since we'll use email for authentication.

  • email: Set as the unique identifier for authentication.

  • profile_picture: An optional field to store the user's profile picture.

  • role: A field to store the user's role, with choices defined by UserRoles.

  • USERNAME_FIELD: Specifies the field used for authentication (email in this case).

  • REQUIRED_FIELDS: Additional fields required when creating a superuser.

  • objects: Specifies the custom manager we created earlier.

Updating AUTH_USER_MODEL in Settings

To tell Django to use our custom user model, add the following line to your settings.py:

# projectpulse/settings.py

AUTH_USER_MODEL = 'accounts.CustomUser'

Applying Migrations

Now that we've defined our custom user model, we need to create and apply migrations to update the database.

1. Create Migrations

Run the following command to create migration files:

python manage.py makemigrations

You should see output similar to:

Migrations for 'accounts':
  accounts/migrations/0001_initial.py
    - Create model CustomUser

2. Apply Migrations

Apply the migrations to update the database schema:

python manage.py migrate

This will create the necessary tables in your database for the accounts app.

Creating a Superuser

To test our custom user model, let's create a superuser account.

Run the following command:

python manage.py createsuperuser

You'll be prompted to enter the following information:

  • Email address: Provide a valid email.

  • First name and Last name: Since we added these to REQUIRED_FIELDS.

  • Password: Enter a secure password.

Email address: admin@example.com
First name: Admin
Last name: User
Password: ********
Password (again): ********
Superuser created successfully.

What's Next?

In this tutorial, we've:

  • Registered the accounts app in our project settings.

  • Created a custom user model with additional fields.

  • Applied migrations to update the database.

  • Created a superuser to test the new user model.

In the next article, we'll focus on creating custom forms for our CustomUser and integrating them into the Django admin panel for easier management.

Conclusion

Congratulations! You've successfully implemented a custom user model in Django, which is a significant step in customizing your web application to meet specific requirements. Custom user models offer flexibility and scalability, allowing you to add as many custom fields as your project needs

Additional Resources

Feel free to leave comments or questions below. Happy coding!

Did you find this article valuable?

Support Slice-Of-Code by becoming a sponsor. Any amount is appreciated!