Django — email user authenthication
-
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" %} »</button>
</form>
{% endblock %}