Django login with email instead of username

The purpose of this tutorial is show you how to replace the username field of django authentication system with the email field(making it unique) to authenticate and authorize the users. So without further a do let's get started.

Start a new project

Just type the below commands into your terminal/shell.

django-admin startproject customauth
cd customauth
python manage.py startapp accounts

Edit Settings.py

Now We also need to add 'core' app to INSTALLED_APPS list inside the settings file.

customauth/settings.py
INSTALLED_APPS = [
    ...
    'accounts',
]

Create a custom user model

To create the custom user django has provided the AbstractUser class to us. So all we need to do is inherit that class and override/create our own fields.

accounts/models.py
from django.db import models
from django.contrib.auth.models import User, AbstractUser


class CustomUser(AbstractUser):
    username = models.CharField(max_length=150, unique=False, blank=True, null=True)	
    email = models.EmailField(unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def __str__(self):
        return self.email

Here we override the email field(Making it unique) and username field(Making it non-unique) and set the USERNAME_FIELD(A string describing the name of the field on the user model that is used as the unique identifier.) to 'email'.

Edit settings.py

To tell django about our custom user model. we need to set the AUTH_USER_MODEL to "Appname:UserModelName" inside the settings.py file.

customauth/settings.py
AUTH_USER_MODEL = 'accounts.CustomUser'

Its time to run the migrations and start using the custom model into our project.

python manage.py makemigrations
python manage.py migrate

Implement Authentication System

So in this section of tutorial, we'll be using the custom user model to set up the authentication system for our project(login, register, logout, profile functionality).So let's start with the forms.

Create forms

Create a new file named 'forms.py' inside the accounts app directory and paste the below in it.

accounts/forms.py
from accounts.models import CustomUser
from django.contrib.auth.forms import UserCreationForm

from django import forms

class LoginForm(forms.Form):
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput())

class CustomUserCreationForm(UserCreationForm):
    class Meta:
        model = CustomUser
        fields = ("email",)

we'll be using CustomUserCreationForm(which inherits from django's UserCreationForm) to register the user and LoginForm to log the user in.

Let's move on to implement the login view that will handle the logging in functionality for our project. The code is straightforward and explained by the relevant comments(wherever necessary).

accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages

from accounts.forms import LoginForm, CustomUserCreationForm

def login_view(request):
    if request.method == "POST":
        form = LoginForm(request.POST)
        # check if form is valid
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            # Check if credentials are valid or not.
            user = authenticate(request,email = email,password = password)
            if user:
                # If valid then log the user in and redirect to the profile page.
                login(request,user)
                return redirect("accounts:profile")
            else:
                # Using the django messages
                messages.error(request, 'Invalid Email/Password.')
    else:
        form = LoginForm()
    return render(request,"login.html", {'form': form})

Let's move on to implement the sign up view

accounts/views.py
def signup_view(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('accounts:login')
    else:
        form = CustomUserCreationForm()
    return render(request, 'signup.html', {'form': form})

Finally, logout and profile views.

accounts/views.py
def logout_view(request):
    logout(request)
    return redirect("accounts:login")

@login_required
def profile(request):
    return render(request, "profile.html")

Create Html Templates

To create the frontend html templates. create a new directory/folder inside the accounts app and create three files 'login.html', 'signup.html' and 'profile.html'.

accounts/templates/login.html
<h1>Login</h1>
{% if messages %}
    {% for message in messages %}
            {{ message|escape }}
    {% endfor %}
{% endif %}
<form method="POSt">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Submit">
</form>

Here we have used the django messages to display the 'invalid email/password' error(If credentials are wrong).

accounts/templates/profile.html
<h1>Signup</h1>
<form method="POSt">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Submit">
</form>
accounts/templates/signup.html
<h1>Welcome {{ request.user.email }}</h1>

Configure Urls.py

Now let's create the urls(mapping between URLs and views) for our application. Note: I would like to use the common django convention of keeping the each app's urls in the seperate urls.py file(Inside the app directory).

customauth/urls.py
from django.contrib import admin
from django.urls import path, include

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

Now create a new 'urls.py' file inside the accounts app directory and paste the below code.

accounts/urls.py
from django.urls import path
from accounts import views

app_name = "accounts"

urlpatterns = [
    path("login/", views.login_view, name="login"),
    path("signup/", views.signup_view, name="signup"),
    path("logout/", views.logout_view, name="logout"),
    path("profile/", views.profile, name="profile"),

]

At last, let's start the django development server(Just paste the below command in your terminal).

python manage.py runserver

Finally, it's time to check if everything is working or not. just go to http://127.0.0.1:8000/signup/ and you should be ready to go on your own.

output of Sign up with email(Django)