Understanding user models in Django

Understanding user models in Django

As you build more dynamic and complex web applications with Django, managing users, securing content, and controlling access becomes essential. The Django User model offers a solid foundation for implementing these features efficiently and securely.

In this article, we’ll explain what the Django User model is, how to set it up, and how to customize it to suit your needs. We’ll also discuss user permissions and authorization to provide a strong application security.

What is the Django User model?

Django’s built-in authentication system simplifies user management with features like login, logout, and password management.

By default, Django offers a User model and authentication views that handle most of these tasks out of the box. For user registration, you can add custom functionalities to suit your application’s needs.

Using the default Django User model

Choosing the default User model over a custom one can speed up development, as it offers ready-made features like authentication, permissions, and password management without requiring extra setup.

This makes it a great choice for projects with simple user requirements.

Overview of user authentication features

The default Django User model includes essential features for user authentication and management, such as fields for username, password, email, and permissions (like is_staff and is_superuser flags).

It provides built-in methods for password hashing and integrates with Django’s authentication framework to handle login, logout, and access control. Combined with the permissions and session frameworks, it simplifies managing user sessions and roles securely.

Set up user login and logout

Django’s built-in views in django.contrib.auth.views make it easy to implement login and logout functionality. The LoginView and LogoutView handle the authentication process, reducing the need for custom code and simplifying these operations.

  1. Set up URLs for login and logout

Edit your urls.py file:

from django.urls import path
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('login/', auth_views.LoginView.as_view(), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]

The URL pattern /login/ is mapped to LoginView, which handles user login by displaying a login form and processing authentication.

Meanwhile, /logout/ is mapped to LogoutView, which logs the user out, clears the session, and redirects them to the specified page.

  1. Create a login form template

You can create a simple login template (login.html) inside your app’s templates directory:

<form method="post" action="{% url 'login' %}">
    {% csrf_token %}
    <div>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
    </div>
    <div>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
    </div>
    {% if form.non_field_errors %}
        <div class="error">
            {{ form.non_field_errors }}
        </div>
    {% endif %}
    <button type="submit">Login</button>
</form>

This template serves as a user interface for submitting login credentials. When paired with Django’s built-in LoginView, it processes the username and password to authenticate the user.

  1. Create logout

You don’t need a separate template to log out, as Django’s LogoutView handles it automatically. It clears the user session and redirects to the page defined in the LOGOUT_REDIRECT_URL setting, or a specified next_page.

To do so, simply add a logout link in your template:

<a href="{% url 'logout' %}">Logout</a>

Create user registration with UserCreationForm

Django doesn’t provide a built-in registration view by default, but you can create one using UserCreationForm. This Django form, provided in django.contrib.auth.forms, includes fields for username, password1, and password2, and handles validation for creating a new user.

  1. Create a registration view

In your views.py file, add:

from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
from django.contrib import messages

def register(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, "Registration successful. Welcome!")
            return redirect('home')
        else:
            messages.error(request, "Registration failed. Please check the form for errors.")
    else:
        form = UserCreationForm()
    return render(request, 'register.html', {'form': form})
  1. Set up the URL for registration

Include these lines in urls.py:

from django.urls import path
from . import views  # Import your views module

urlpatterns = [
    path('register/', views.register, name='register'),
]
  1. Create the registration form template

For the register.html file, include:

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Register</button>
</form>

This template displays a simple form rendered from Django’s UserCreationForm. When paired with the registration view, it lets users create an account.

After successful registration, if the view includes a login step, users are automatically logged in and redirected to the specified page.

Manage user sessions

Once users are logged in, manage their sessions to maintain security and ensure a smooth user experience.

Django uses the session framework to store data about each user’s session on the server. This includes whether the user is logged in, their authentication state, and any additional session data you choose to save.

  1. Access and modify session data

Store, retrieve, and delete session data in Django views using the request.session dictionary-like object:

from django.shortcuts import render

def user_dashboard(request):
    # Store data in session
    request.session['welcome_message'] = 'Welcome back!'
    
    # Retrieve data from session
    welcome_message = request.session.get('welcome_message', 'Hello, User!')

    # Remove data from session
    if 'welcome_message' in request.session:
        del request.session['welcome_message']

    return render(request, 'dashboard.html', {'message': welcome_message})
  1. Customize session data

Control session behavior by configuring the following settings in settings.py:

  • SESSION_COOKIE_AGE – determines how long a session lasts. By default, it’s 1209600 seconds or 2 weeks.
  • SESSION_EXPIRE_AT_BROWSER_CLOSE – if set to True, sessions expire when the user closes the browser.
  • SESSION_ENGINE – specifies the session backend, such as database, cache, or file-based.
# settings.py
SESSION_COOKIE_AGE = 3600  # Sessions expire after 1 hour
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # Sessions expire on browser close
SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # Use database for sessions
  1. Check authentication state

Determine if a user is logged in using request.user.is_authenticated, useful for protecting views or displaying personalized content:

from django.shortcuts import redirect

def protected_view(request):
    if not request.user.is_authenticated:
        return redirect('login')  # Redirect to login if not authenticated
    return render(request, 'protected_page.html')
  1. End a session

Log out users and clear their session data using the logout function:

from django.contrib.auth import logout
from django.shortcuts import redirect

def logout_view(request):
    logout(request)  # Logs out the user and clears session data
    return redirect('login')  # Redirect to login page

Customizing the Django User model

In some cases, Django’s default User model may not meet all the requirements of your application. Customizing the user model lets you add fields, change authentication behavior, or replace the User model entirely with one tailored to your needs.

AbstractBaseUser vs. AbstractUser

When customizing the user model, you have two primary options: extending from AbstractBaseUser or AbstractUser.

AbstractBaseUser provides the bare minimum: password hashing and authentication features. It gives you full control to define all fields and behaviors, including username, email, or any additional data you need.

However, it requires you to implement fields and methods such as USERNAME_FIELD, is_active, and objects.

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError('Email address is required')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

class CustomUser(AbstractBaseUser):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

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

    objects = CustomUserManager()

AbstractUser inherits from AbstractBaseUser and includes all fields and methods of the default User model, such as username, email, first_name, and last_name. It’s ideal if you only need to add extra fields or slightly modify existing behavior without rewriting the entire model.

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

class CustomUser(AbstractUser):
    age = models.PositiveIntegerField(null=True, blank=True)

Create a custom user model

To define a custom user model, follow these steps:

  1. Define a custom user model extending from AbstractBaseUser or AbstractUser.
  2. Specify your custom user model in settings.py:
AUTH_USER_MODEL = 'myapp.CustomUser'
  1. Custom user models require a custom manager to handle user creation, create_user and create_superuser.
  2. Use UserCreationForm and UserChangeForm from django.contrib.auth.forms as bases for creating forms compatible with your custom user model:
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):
    class Meta:
        model = CustomUser
        fields = ['email', 'first_name', 'last_name']

class CustomUserChangeForm(UserChangeForm):
    class Meta:
        model = CustomUser
        fields = ['email', 'first_name', 'last_name']

Migrate to a custom user model in an existing project

Switching to a custom user model in an existing project can be challenging, as Django’s authentication system tightly integrates with the default User model. Here are the steps:

  1. Back up your database to avoid losing data during migration.
  2. Define your custom model and update the AUTH_USER_MODEL setting in settings.py.
  3. If the project already has a User model and associated data, you’ll need to handle the migration process carefully:
    • Create a data migration script to copy data from the old auth.User model to your new custom user model.
    • Use makemigrations and migrate commands to apply the changes.
  4. Update any models, views, or forms that reference the default User model to point to your custom model using get_user_model():
from django.contrib.auth import get_user_model

User = get_user_model()
  1. Thoroughly test all authentication features, including login, logout, and password reset. Make sure they work seamlessly with the new user model.

Managing user permissions and authorization

Django lets you assign different permissions to individual users and groups, enabling role-based access control.

Control user authorization with Django’s built-in permissions

You can assign permissions to user models or groups and check within your views to control access to specific actions and define their roles.

For example, an admin might have permissions to add or delete content, while regular users can only view content.

Restrict views based on permissions

You can restrict access to certain views by checking if a user has the required permissions using Django’s @permission_required decorator. For example:

from django.contrib.auth.decorators import permission_required

@permission_required('app.add_article')
def add_article(request):
    # View code for adding an article

Only users with the add_article permission will be able to access this view.

Implement role-based access control (RBAC) with groups

Django’s Group feature lets you implement role-based access control by creating roles such as editor or viewer and associating users with these groups. You can give groups specific permissions, which are automatically inherited by all their members.

  1. Assign users to groups

To assign a user to a group programmatically, use the Group and User models as shown below:

from django.contrib.auth.models import Group, User

# Retrieve the group and user
group = Group.objects.get(name='editors')
user = User.objects.get(username='john')

# Assign the user to the group
user.groups.add(group)

If the group doesn’t exist, you can create it programmatically:

group, created = Group.objects.get_or_create(name='editors')
  1. Check group membership in views

You can restrict views based on group membership by using the @user_passes_test decorator. The example below demonstrates how to limit access to a view for users in the editors group:

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

# Define a test function to check group membership
def is_editor(user):
    return user.groups.filter(name='editors').exists()

# Restrict access to the editor_dashboard view
@user_passes_test(is_editor, login_url='/login/')  # Redirect unauthorized users to login
def editor_dashboard(request):
    return render(request, 'editor_dashboard.html')

In this example, only users who belong to the editors group can access the editor_dashboard view. Unauthorized users are redirected to the login page (/login/), but you can customize this URL as needed.

Working with Django’s authentication backends

Authentication backends in Django determine how users are authenticated and how permissions are retrieved. By default, Django uses the ModelBackend, which authenticates against the User model and checks permissions stored in the database.

Create a custom authentication backend

You can create a custom authentication backend to implement alternative login methods, such as email-based authentication or third-party services. A custom backend must define at least two methods:

  • authenticate(self, request, **credentials)
  • get_user(self, user_id)

Here’s an example of creating a custom backend to allow login with an email address:

from django.contrib.auth.models import User

class EmailBackend:
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(email=username)  # Authenticate using email
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Add this backend to your AUTHENTICATION_BACKENDS setting:

AUTHENTICATION_BACKENDS = [
    'myapp.backends.EmailBackend',
    'django.contrib.auth.backends.ModelBackend',  # Fallback to username authentication
]

Work with permissions in custom backends

Custom backends can also define how permissions are retrieved for users by overriding has_perm, has_module_perms, or similar methods. For instance:

def has_perm(self, user, perm, obj=None):
    # Custom permission logic
    return True  # Grant all permissions for demonstration purposes

Debug authentication issues

Django supports multiple authentication backends, which showed in the AUTHENTICATION_BACKENDS snippet. However, you might find issues when using multiple backends. To debug:

  • Check the logs to see which backend is being called.
  • Use django.contrib.auth.authenticate() manually in the shell to test credentials:
from django.contrib.auth import authenticate
user = authenticate(username='john@example.com', password='securepassword')
print(user)

Conclusion

Understanding and customizing Django’s User model is important for building strong authentication systems. With flexible tools for creating custom user models, managing sessions, permissions, and authentication backends, Django supports diverse requirements.

You can implement role-based access control, restrict views, and tailor authentication methods to provide a secure and user-friendly experience. Mastering these features lets you manage users confidently in any Django application.

Django user models FAQ

How do I create a custom user model in Django?

To create a custom user model in Django, subclass AbstractUser or AbstractBaseUser, add desired fields, and set AUTH_USER_MODEL in settings.py. Make sure this is done before initial migrations to avoid conflicts. This setup allows for custom user attributes and flexible authentication.

What fields are included in the default Django User model?

The default Django User model includes username, password, email, first_name, and last_name for basic user information; is_staff, is_active, and is_superuser for role management; and last_login and date_joined for activity tracking.

How do I extend the default User model?

Extend Django’s default User model by creating a new model with a OneToOneField(User). Add custom fields in this model to store additional data, keeping the original user model intact while allowing access to extended attributes through the user instance.

Author
The author

Andzelika D.

Andzelika is a Content Writer and Editor with over 5 years of experience in the digital marketing industry. With passion for technology and the art of the written word, she loves to combine the two worlds and produce actionable content.

Author
The Co-author

Ariffud Muhammad

Ariffud is a Technical Content Writer with an educational background in Informatics. He has extensive expertise in Linux and VPS, authoring over 200 articles on server management and web development. Follow him on LinkedIn.