PROJECTS NOTES HOME

Creating a custom user model in django

NOTE 1 - Prerequisite to this project is here, first - understand the basics.

NOTE 2 - This is basically a copy of https://learndjango.com/tutorials/django-custom-user-model tutorial. Wanted to keep a copy of it for myself, since this information is useful.

NOTE 3: for me this post is just a reference of how it can be done IF NOT using django's "allauth" package. If using "allauth" - I still have not figured out how to implement custom fields to the user model.

But if not using "allauth package" - this tutorial will help you out.


Django ships with a built-in User model for authentication.

However, for a real-world project, the official Django documentation highly recommends using a custom user model instead; it provides far more flexibility down the line so, as a general rule

always use a custom user model for all new Django projects

There are two modern ways to create a custom user model in Django: AbstractUser and AbstractBaseUser. In both cases, we can subclass them to extend existing functionality; however, AbstractBaseUser requires much, much more work. Seriously, only mess with it if you know what you're doing. And if you did, you wouldn't be reading this tutorial, would you?

So we'll use AbstractUser, which subclasses AbstractBaseUser but provides more default configuration.

Creating our initial custom user model requires four steps:

In settings.py, we'll use the AUTH_USER_MODEL config to tell Django to use our new custom user model instead of the built-in User model. We'll call our custom user model CustomUser.

# project/settings.py
AUTH_USER_MODEL = "accounts.CustomUser"  # new

Now update accounts/models.py with a new User model, which we'll call CustomUser.

"""A module to register account app models to django admin."""

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


class CustomUser(AbstractUser):
    """Account model."""

    date_of_birth = models.DateField(null=True, blank=True)
    # add additional fields in here

We need new versions of two form methods that receive heavy use working with users. Create a new file accounts/forms.py. We'll update it with the following code to largely subclass the existing forms.

# accounts/forms.py
"""A module for auth page forms. They are later used in the views.py"""

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

from apps.accounts.models import CustomUser


# pylint: disable=too-few-public-methods
class CustomUserCreationForm(UserCreationForm):
    """A form for user creation"""

    class Meta:
        """Additional settings for the Meta?"""

        model = CustomUser
        fields = ("username", "email", "date_of_birth")

    date_of_birth = forms.DateField(
        widget=forms.DateInput(attrs={"type": "date"}),
    )


# pylint: disable=too-few-public-methods
class CustomUserChangeForm(UserChangeForm):
    """A form for user change"""

    class Meta:
        """Additional settings for the Meta?"""

        model = CustomUser
        fields = ("username", "email", "date_of_birth")

    date_of_birth = forms.DateField(
        widget=forms.DateInput(attrs={"type": "date"}),
    )

Finally, we update admin.py since the admin is highly coupled to the default User model.

# accounts/admin.py

"""A module to register users app models to django admin."""

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from apps.accounts.forms import CustomUserChangeForm, CustomUserCreationForm
from apps.accounts.models import CustomUser


class CustomUserAdmin(UserAdmin):
    """A modification to the default account model admin."""

    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser

    fieldsets = (
        (None, {"fields": ("username", "password")}),
        (
            "Personal info",
            {"fields": ("first_name", "last_name", "email", "date_of_birth")},
        ),
        (
            "Permissions",
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                )
            },
        ),
        ("Important dates", {"fields": ("last_login", "date_joined")}),
    )

    list_display = [
        "email",
        "username",
        "date_of_birth",
    ]


admin.site.register(CustomUser, CustomUserAdmin)

And we're done! We can now run makemigrations and migrate (clear the db and migrations if you are doing makemigrations not for the first time, we need to start fresh here) to create a new database that uses the custom user model.

python manage.py makemigrations accounts
python manage.py migrate

The last step is our views.py file in the accounts app which will contain our signup form. We will modify the already created form.

"""A module for accounts app views."""

from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import CreateView

from apps.accounts.forms import CustomUserCreationForm


class SignUpView(CreateView):
    """Generic CBV view for account create page"""

    form_class = CustomUserCreationForm
    success_url = reverse_lazy("login")
    template_name = "registration/signup.html"