User Registration

The previous lesson leaned on LoginView and LogoutView — both handed to you, ready-made, by django.contrib.auth. A sign-up page is different: Django deliberately does not ship a built-in registration view. That's not an oversight — every project asks for different things at sign-up (some want just a username and password, others want an email, a display name, terms-of-service acceptance, or more) and Django's maintainers chose not to guess which shape yours needs. What it does give you is UserCreationForm, a ready-made ModelForm for the User model, which is normally enough to build a working sign-up view in a few lines.

A Registration View with UserCreationForm

UserCreationForm already validates a username, checks the two password fields match, and hashes the password correctly before saving — all of the tricky, security-sensitive parts are handled. The view around it follows the exact same GET/POST shape as any other form-driven view:

# taskflow/views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm

def register(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            new_user = form.save()
            return redirect('login')
    else:
        form = UserCreationForm()

    return render(request, 'registration/register.html', {'form': form})

form.save() here creates a genuine new row in the User table, with the password already hashed — never the raw text the visitor typed. As written, a freshly registered visitor lands on the login page and has to sign in separately, which works, but is one extra step most apps skip.

Logging the New User In Immediately

Django's login() function starts a session for a given user directly, without requiring a fresh username/password check — exactly what's needed right after registration, since the visitor just proved who they are by successfully creating the account. Calling it turns the view above into a true "register and land inside the app" flow:

# taskflow/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm

def register(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            new_user = form.save()
            login(request, new_user)
            return redirect('task-list')
    else:
        form = UserCreationForm()

    return render(request, 'registration/register.html', {'form': form})

After this, request.user on the very next request is the newly created account — the same request.user concept covered in User Authentication, now populated the moment sign-up finishes rather than requiring a separate login step.

UserCreationForm's Default Fields Are Minimal

Out of the box, UserCreationForm only asks for a username and a password (entered twice, to catch typos). Most real apps want at least an email address too, and the standard way to add that is subclassing the form rather than rebuilding it from scratch:

# taskflow/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class TaskFlowRegistrationForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta(UserCreationForm.Meta):
        model = User
        fields = ['username', 'email', 'password1', 'password2']

Swapping this form into the register view above needs no other changes — the same form.is_valid() / form.save() / login() sequence works identically, now collecting an email address along with the username and password.

Password Validation Happens Automatically

Every password submitted through UserCreationForm is checked against Django's AUTH_PASSWORD_VALIDATORS setting — a list of validator classes that reject things like an overly short password, a password too close to the username, or one of the most commonly used passwords, before the account is ever created. This runs automatically as part of form.is_valid(); there's nothing extra to call. It's enough, at this stage, just to know it's there and where it lives in settings.py if the rules ever need adjusting.

When You Need More: django-allauth

The registration flow built above covers the common case well, but real products often need more: email verification links, password reset flows with extra polish, or "Sign in with Google / GitHub" buttons. Rather than building all of that by hand, most Django projects that need it reach for a third-party package — django-allauth being the most widely used — which handles registration, email verification, and social login as a packaged, configurable add-on. It's worth knowing this exists once TaskFlow's needs grow past the basics, though it's a large enough topic to deserve its own dedicated reading outside this track.

Next up: django.contrib.auth's built-in User model covers a lot, but it can't be extended with new fields directly — that requires swapping in a Custom User Model, next.