Kabads

Django custom users

I’ve been looking at Django recently and I wanted to share some notes on how to create custom users. Django comes with a built-in user model, but sometimes you need to extend it or create a new one. Here’s how you can do it.

Basically there will be several steps to follow:

The files for this blog post can be found here: https://github.com/kabads/django-custom-user-example.

Optional: Create a new Django project

If you don’t have a Django project yet, you can create one with the following command:

$ django-admin startproject myproject
$ cd myproject

Create a custom user model

I went for the method of creating a new app in my project to manage users - called accounts:

$ python manage.py startapp accounts

WARNING: At this point, you may be tempted to create your superuser (python manage.py createsuperuser), but you’ll get an error because Django doesn’t know about your custom user model yet. Hold off that and create your custom user model first.

Create a custom user model

To create a custom user model, you need to create a new model that inherits from AbstractBaseUser and PermissionsMixin. Here’s an example:

# accounts/models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
from django.utils import timezone

# Create your models here.

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        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):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        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.')

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

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=30, blank=True)
    last_name = models.CharField(max_length=30, blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(default=timezone.now)
    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

Obviously, you can edit the model to suit all your needs (with extra fields, etc.).

Update the myproject/settings.py file

Then I added the app to the INSTALLED_APPS list in the myproject/settings.py file:

INSTALLED_APPS = [
    ...
    "accounts",
]

Ensure you have the following line in your myproject/settings.py file:

# settings.py
...
AUTH_USER_MODEL = 'accounts.CustomUser'

Create Views for the custom user model

# accounts/views.py
from django.shortcuts import render

# Create your views here.

from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate
from .forms import CustomUserCreationForm, CustomAuthenticationForm

def register(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('home')
    else:
        form = CustomUserCreationForm()
    return render(request, 'accounts/register.html', {'form': form})

def login_view(request):
    if request.method == 'POST':
        form = CustomAuthenticationForm(request, data=request.POST)
        if form.is_valid():
            user = authenticate(request, email=form.cleaned_data['username'], password=form.cleaned_data['password'])
            if user is not None:
                login(request, user)
                return redirect('home')
    else:
        form = CustomAuthenticationForm()
    return render(request, 'accounts/login.html', {'form': form})

from django.shortcuts import render

def home(request):
    return render(request, 'home.html')

Update the admin configuration in accounts/admin.py and register the custom user model

# accounts/admin.py

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

class CustomUserAdmin(UserAdmin):
    # Define the fields to be used in displaying the User model.
    list_display = ('email', 'first_name', 'last_name', 'is_staff', 'is_active')
    list_filter = ('is_staff', 'is_active')
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('first_name', 'last_name')}),
        ('Permissions', {'fields': ('is_staff', 'is_active', 'is_superuser', 'groups', 'user_permissions')}),
        ('Important dates', {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'first_name', 'last_name', 'password1', 'password2', 'is_staff', 'is_active'),
        }),
    )
    search_fields = ('email', 'first_name', 'last_name')
    ordering = ('email',)

admin.site.register(CustomUser, CustomUserAdmin)

Create the Migrations

Now we have the models, we need to create a migration to initialize the database. Run the following commands:

$ python manage.py makemigrations
$ python manage.py migrate

NOTE: Now you can create the superuser. If you did this before, you will have an error.

$ python manage.py createsuperuser

Then you can run the development server and try and login:

$ python manage.py runserver

You can now go to the admin page and see your custom user model - at http://127.0.0.1:8000/admin/.