Django — email user authenthication

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 email_auth
cd email_auth
django-admin startproject src .
python manage.py startapp accounts
python manage.py runserver

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’

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
python manage.py createsuperuser

At this point we, in Django-custom user authentication , have to proceed to design templates, views and urls for Django ‘auth’ modules. Instead, we are going to use django-allauth.

pip install django-allauth

Update ‘src/settings.py’

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',    'crispy_forms',    
    'accounts',
]

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

AUTHENTICATION_BACKENDS = (
    # Needed to login by username in Django admin, regardless of `allauth`
    "django.contrib.auth.backends.ModelBackend",

    # `allauth` specific authentication methods, such as login by e-mail
    "allauth.account.auth_backends.AuthenticationBackend",
)

SITE_ID = 1

ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_UNIQUE_EMAIL = True

Run

python manage.py migrate

Update ‘/src/urls.py’

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('allauth.urls')),
]

Now we can access page ‘http://127.0.0.1:8000/accounts/login/’ which does not have a pretty template and after login we are redirected to url ‘/accounts/profile’ which is not defined so we have do design templates for login and logout procedures and a home page to redirect instead of ‘/accounts/profile’.

First, update ‘/src/settings’ in order to acces root templates directory:

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

create ‘/templates/home.html’ (reverse links ‘account_logout’, etc are defined in django-allauth docs)

{% extends 'base.html' %}

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

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

Update ‘/src/urls.py’:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('allauth.urls')),
    path('', include('accounts.urls')),
]

Create ‘/accounts/urls.py’

from django.urls import path

from . import viewsurlpatterns = [
    path('', views.home, name = 'home'),
]

And ‘/accounts/views.py’

from django.shortcuts import render

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

Now, home page is accesible at ‘localhost:8000’.

The project works with default django-allauth templates if there is no correspondig template create in ‘/email_auth/templates/accounts’ project directory. In order to direct customize this files, download the source of django-allauth and copy ‘django-allauth-master/allauth/templates/account’ to ‘/email_auth/templates/accounts’.

We coud add a some ‘style’ by adding Bootstrap 4 necessary file to base.html and adjusting forms (that mean all {{ form.as_p }} fields from the templates which are not equal in length) to {{ form|crispy }}

A string search in ‘/templates/account/’:

grep  'form.as_p'  `find . -type f -name "*.*"` | less

return us the templates which needs adjusting the forms:

./email.html:        {{ form.as_p }}
./login.html:  {{ form.as_p }}
./password_change.html:        {{ form.as_p }}
./password_reset.html:        {{ form.as_p }}
./password_reset_from_key.html:                {{ form.as_p }}
./password_set.html:        {{ form.as_p }}
./signup.html:  {{ form.as_p }}
<!DOCTYPE html>
<html>
  <head>

    <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 head_title %}{% endblock %}</title>
    {% block extra_head %}
    {% endblock %}
  </head>
  <body>

    <div class="container">

    {% block body %}

    {% if messages %}
    <div>
      <strong>Messages:</strong>
      <ul>
        {% for message in messages %}
        <li>{{message}}</li>
        {% endfor %}
      </ul>
    </div>
    {% endif %}

    <div>
      <strong>Menu:</strong>
      <ul>
        {% if user.is_authenticated %}
        <li><a href="{% url 'account_email' %}">Change E-mail</a></li>
        <li><a href="{% url 'account_logout' %}">Sign Out</a></li>
        {% else %}
        <li><a href="{% url 'account_login' %}">Sign In</a></li>
        <li><a href="{% url 'account_signup' %}">Sign Up</a></li>
        {% endif %}
      </ul>
    </div>
    {% block content %}
    {% endblock %}
    {% endblock %}
    {% block extra_body %}
    {% endblock %}

    </div>

  </body>
</html>

and signup.html like that

{% extends "account/base.html" %}

{% load crispy_forms_tags %}

{% load i18n %}

{% block head_title %}{% trans "Signup" %}{% endblock %}

{% block content %}
<h1>{% trans "Sign Up" %}</h1>

<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>

<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
  {% csrf_token %}
  {{ form|crispy }}
  {% if redirect_field_value %}
  <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
  {% endif %}
  <button type="submit">{% trans "Sign Up" %} &raquo;</button>
</form>

{% endblock %}

Resources:


Return to home