How to Use Email as Username for Django Authentication
Tags: python, django, authentication, auth
Published: 2020-04-22 14:59:59

---
title: How to Use Email as Username for Django Authentication
published: true
date: 2020-01-23 00:00:00 UTC
tags: python, django, authentication, auth
canonical_url: https://tech.serhatteker.com/post/2020-01/email-as-username-django/
---

## 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:

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

    ```bash
    $ virtualenv -p python3 .venv
    ```

    You can use [Poetry] or [Pipenv] to create venv as well.
3. Activate virtualenv:

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

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

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

    ```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:

```py
# 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)

```

### 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`:

```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
```

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]:

```python
AUTH_USER_MODEL = 'users.CustomUser'
```

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

```bash
$ python manage.py makemigrations
$ python manage.py migrate
```

__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` :

```python
# 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',)
```

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

Now you can run the app:

```bash
$ python manage.py runserver
```

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

```bash
$ python manage.py createsuperuser
Email address: user@test.api
Password:
Password (again):
Superuser created successfully.
```

All done!


[User Model]: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#user-model
[Custom User Model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project
[Poetry]: https://python-poetry.org/
[Pipenv]: https://pipenv.pypa.io/en/latest/
[django-template repo]: https://github.com/serhatteker/django-template
[Referencing User Model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#referencing-the-user-model
[Manager]: https://docs.djangoproject.com/en/dev/topics/db/managers/