Django — custom user authentication

Dec. 29, 2020

  • server1.jpg Back-End

Initiate a python virtual environment and start a new project:

python -m venv env
source env/bin/activate
pip install Django
pip install django-crispy-forms # for nice forms
mkdir custom_auth
cd custom_auth
django-admin startproject src .
python manage.py startapp accounts
python manage.py runserver

We do not migrate until the configuration of custom user which will be designed by subclassing AbstractUser which subclasses AbstractBaseUser.

Update /src/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'crispy_forms',    
    'accounts',
]
...
AUTH_USER_MODEL = 'accounts.CustomUser'

Update ‘/accounts/models.py’

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

class CustomUser(AbstractUser):
    # add additional fields in here
    description = models.CharField(max_length=100, blank=True)

    def __str__(self):
        return self.username

Update ‘/accounts/forms.py’

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

class CustomUserCreationForm(UserCreationForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email', 'description')

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email', 'description')

Update ‘/src/admin.py’ since Admin is linked to the users definitions.

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

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

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = ['email', 'username',]

admin.site.register(CustomUser, CustomUserAdmin)

Run:

python manage.py makemigrations
python manage.py migrate

Create superuser:

python manage.py createsuperuser

Update /src/settings.py adding:

TEMPLATES = [
    {
        ...
        'DIRS': [str(BASE_DIR.joinpath('templates'))],
        ...
    },
]
...
LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'home'

Create directory ‘custom_auth/templates/registration’ and edit ‘/templates/base.html’ (using Bootstrap 4 starter template):

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <title>{% block title %}Django Auth Tutorial{% endblock %}</title>
  </head>
  <body>

    <div class="container">
        <main>
        {% block content %}
        {% endblock %}
        </main>
    </div>
    
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </body>
</html>

Create ‘/templates/home.html’

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}
{% if user.is_authenticated %}
  Hi {{ user.username }}!
  <p><a href="{% url 'logout' %}">Log Out</a></p>
{% else %}
  <p>You are not logged in</p>
  <a href="{% url 'login' %}">Log In</a> |
  <a href="{% url 'signup' %}">Sign Up</a>
{% endif %}
{% endblock %}

‘/templates/registration/login.html’

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Login{% endblock %}

{% block content %}
<h2>Log In</h2>
<form method="post">
  {% csrf_token %}
  {% comment %}{{ form.as_p }}{% endcomment %}
  {{ form|crispy }}
  <button type="submit">Log In</button>
</form>
{% endblock %}

‘/templates/registration/signup.html’

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Sign Up{% endblock %}

{% block content %}
<h2>Sign Up</h2>
<form method="post">
  {% csrf_token %}
  {% comment %}{{ form.as_p }}{% endcomment %}
  {{ form|crispy }}  
  <button type="submit">Sign Up</button>
</form>
{% endblock %}

Update ‘/src/urls.py’

from django.contrib import adminfrom django.urls import path, includeurlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('accounts.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
]

Create ‘/accounts/urls.py’

from django.urls import pathfrom . import viewsurlpatterns = [
    path('', views.home, name = 'home'),
    path('accounts/signup/', views.signup, name='signup'),
]

Update ‘/accounts/views.py’

from django.shortcuts import render

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

from django.urls import reverse
from django.shortcuts import redirect
from .forms import CustomUserCreationForm

def signup(request):

    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect(reverse("login"))
        else:
            return redirect(reverse("home"))

    form = CustomUserCreationForm()
    return render(request, "registration/signup.html", {"form":form})

Access ‘localhost:8000’.

The end.

Resources:


Return to home