DEV Community

Serhat Teker
Serhat Teker

Posted on • Originally published at tech.serhatteker.com on

How to Use Email as Username for Django Authentication

0: Intro

The aim of this post will be explaining how to create a custom User Model in Django. So we can use that email address as the primary 'user identifier' instead of a username for authentication.

Default Django app will give you a User Model that has a mandatory username field, and an 'optional' email field.

However if you’re starting a new project, Django highly recommends you to set up a custom User Model. For more info on this subject look at official doc: Custom User Model

In addition to that I usually need to set up email as username to
authenticate users in my apps.

1: Setup The Repo

  1. I don't like django's default app template, therefore I created and using mine. You can clone this remote repo to run below code:

    $ git clone https://github.com/serhatteker.com/django-template.git
    
  2. Create a virtual environment named .venv:

    $ virtualenv -p python3 .venv
    

    You can use Poetry or Pipenv to create venv as well.

  3. Activate virtualenv:

    $ source .venv/bin/activate
    
  4. Install requirements:

    $ pip install -r requirements.txt
    
  5. Start an app named 'users':

    $ mkdir src/app && python manage.py startapp src/users
    
  6. Add the new app to the INSTALLED_APPS list into your settings.py:

    MY_APPS = [
        'src.core',
        'src.users',
    ]
    
    INSTALLED_APPS = DJANGO_CORE_APPS + THIRD_PARTY_APPS + MY_APPS
    

2: Creating User Model

2.0 Creating Model Manager

First of all we need to add a custom Manager, by subclassing BaseUserManager which uses an email as the unique identifier instead of a username.

Add below code to your users' app model:

# src/users/model.py
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _


class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of usernames.
    """
    def create_user(self, email, password, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError(_('The Email must be set'))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)

Enter fullscreen mode Exit fullscreen mode

2.1 Creating User Model

2.1.0 AbstractUser vs AbstractBaseUser

As we mentioned before the default User Model in Django uses a username to
uniquely identify a user during authentication. If you'd rather use an email
address, you'll need to create a custom User model by either subclassing
AbstractUser or AbstractBaseUser.

  • AbstractUser: If you are ok with the existing fields on the User Model and just want to remove the username field.
  • AbstractBaseUser: If you want to start from scratch by creating your own, completely new User Model.

2.1.1 Adding AbstractUser

We will use AbstractUser since we only want to remove username field and use django's very well designed User Model.

Add below code into your model.py:

# src/users/model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _


class CustomUser(AbstractUser):
    username = None
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    def __str__(self):
        return self.email
Enter fullscreen mode Exit fullscreen mode

By adding above code we:

  • Created a new class called CustomUser that subclasses AbstractUser
  • Removed the username field
  • Made the email field required and unique
  • Set the USERNAME_FIELD which defines the unique identifier for the User Model to email
  • Specified that all objects for the class come from the CustomUserManager

3: Update Settings

Add below line into your settings.py for Referencing User Model:

AUTH_USER_MODEL = 'users.CustomUser'
Enter fullscreen mode Exit fullscreen mode

4: Create Tables

Create new migrations and migrate them which will create a new database that uses our custom User Model:

$ python manage.py makemigrations
$ python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Note:

It is important that before applying migrations in your app
you must add CustomUser into your model.
Again, I want to emphasize that before you apply your first
migration you must add your 'custom models'.

5: Admin

This is not a necessity; I mean that it doesn't throw an error when running your app. However I believe that it is more than "good-to-have". So we will add as well:

Add below code into your admin.py :

# src/users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import CustomUser


class CustomUserAdmin(UserAdmin):
    model = CustomUser
    list_display = ('email', 'is_staff', 'is_active',)
    list_filter = ('email', 'is_staff', 'is_active',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Permissions', {'fields': ('is_staff', 'is_active')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)
Enter fullscreen mode Exit fullscreen mode

That's it. From now on you can use our CustomUser to authenticate users with email and password.

Now you can run the app:

$ python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

6: Test

When you create a superuser, you should be prompted to enter an email rather than a username:

$ python manage.py createsuperuser
Email address: user@test.api
Password:
Password (again):
Superuser created successfully.
Enter fullscreen mode Exit fullscreen mode

All done!

Latest comments (1)

Collapse
 
poneoneo profile image
Dwingeloo

EXACTLY WHAT I'M looking for thank a lot !