Customize the User Model

Set Up the Django Project

Before we dive into how to implement authentication in Django, we first need to set up our project and apps.Ā 

Both the screencast and the course text contain all of the necessary steps to set up the project. You should find that these steps are familiar to you from your previous Django experience!

Now that you have watched the screencast, code along with the steps in the course text to do it yourself.

First, create the project directory and navigate into it.

~/Ā  → mkdir fotoblog && cd fotoblog

Ā Now set up the Python environment, activate it, and install Django. Also create a Ā requirements.txtĀ  file using Ā pip freezeĀ  so you can recreate the environment elsewhere.

~/fotoblogĀ  → python -m venvĀ  ENV
~/fotoblogĀ  → source ENV/bin/activate
(ENV) ~/fotoblogĀ  → pip install django
(ENV) ~/fotoblog → pip freeze > requirements.txt

Our project will contain two apps, one handling authentication and account management called Ā authenticationĀ  , and another which will host our blog post and photo sharing logicĀ Ā blogĀ .

Let's start the project and create these two apps now.

(ENV) ~/fotoblogĀ  → django-admin startproject fotoblog .
(ENV) ~/fotoblogĀ  → python manage.py startapp authentication
(ENV) ~/fotoblogĀ  → python manage.py startapp blog

Add these apps to the Ā INSTALLED_APPSĀ in settings.

# fotoblog/settings.py
INSTALLED_APPS = [
Ā Ā Ā  'django.contrib.admin',
Ā Ā Ā  'django.contrib.auth',
Ā Ā Ā  'django.contrib.contenttypes',
Ā Ā Ā  'django.contrib.sessions',
Ā Ā Ā  'django.contrib.messages',
Ā Ā Ā  'django.contrib.staticfiles',
Ā Ā Ā  'authentication',
Ā Ā Ā  'blog',
]

Set it up as a Git repository, and make the initial commit.

(ENV) ~/fotoblogĀ  → git init
(ENV) ~/fotoblogĀ  → echo ENV >> .gitignore
(ENV) ~/fotoblogĀ  → echo __pycache__ >> .gitignore
(ENV) ~/fotoblogĀ  → echo db.sqlite3 >> .gitignore
(ENV) ~/fotoblogĀ  → # You may want to add other non-project files and directories to your .gitignore here
(ENV) ~/fotoblogĀ  → git add .
(ENV) ~/fotoblogĀ  → git status
(ENV) ~/fotoblogĀ  → git commit -m Ā initial commit

The project is now set up. Next , let's configure individual users in Django.

Discover the User Model

By convention, data on an individual user is stored in a model calledĀ User. Django provides a default `User` model. This model has many special methods and features, particularly concerning authentication and permissions, that make it seamlessly integrate into the Django framework.Ā Ā 

You can find the default Ā UserĀ  model in Ā django.contrib.auth.modelsĀ .

Here's a quick overview of some of the differentĀ UserĀ  model fields:

  • usernameĀ  - used to log in.

  • first_name

  • last_nameĀ 

  • email

  • passwordĀ  - this is stored as a hash in the database. Never store raw passwords.

  • is_staffĀ  - a boolean; dictates whether a userĀ can log in to the Django admin site.

  • is_activeĀ  - a boolean; it is considered Django best practice to mark users as inactive by setting this attribute toFalseĀ instead of deleting them.

  • is_superuserĀ  - a boolean; superusers are automatically granted all permissions, such as access to the admin site.Ā 

But what if these fields don't fit my use case?

Good question! You may find that you do not require all of these fields. On the other hand, you may want all of them and more!

Luckily, you are not bound to the default model. Let'sĀ see howĀ to customize Ā UserĀ  .

Use a Custom User Model

Even if you think that the default Ā UserĀ  model is good enough, you should always implement a custom Ā UserĀ  model in your project, even if it is identical to the default one.Ā 

This is because it is difficult and complicated to migrate to a custom Ā UserĀ  model after your Django site has been set up and your initial migrations have been run. It requires lots of tricky migrations and an in-depth understanding of SQL. Plans change, and clients alter specifications. Save yourself a headache and set up a custom Ā UserĀ  model at the start of your project.Ā 

When using a custom Ā UserĀ  model, Django provides two base classes that you can extend to meet your specific needs:

  • AbstractUser

  • AbstractBaseUser

AbstractUser

TheĀ AbstractUserĀ class contains all of the fields and methods that the default Ā UserĀ  does.Ā Ā 

If you think the functionality of the default Ā UserĀ  class alone will meet your needs, then using it as a custom Ā UserĀ  model is as simple as this:Ā 

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

ThisĀ provides all the features and fields of the default Ā UserĀ  model and the added flexibility ofĀ being able to add additional fields and methods to it later.

Say, for instance, you also wanted to store a unique 10-digit account number for each user. To do this, just specify it the same asĀ you would a field on any other model.

class User(AbstractUser):
    account_number =  CharField(max_length=10, unique=True)

If you want to add more fields to the Ā UserĀ  class, you can specify them in the same manner.Ā 

But what ifĀ I don't want to use every field provided by the default Ā UserĀ  class?Ā 

ThenĀ you extend the Ā AbstractBaseUserĀ  class instead. Let's have a look at that.Ā 

AbstractBaseUser

TheĀ AbstractBaseUserĀ class contains no fields apart from the Ā passwordĀ . It also comes with a suite of methods to handle authentication (as does Ā AbstractUserĀ ).Ā 

When extending theĀ AbstractBaseUserĀ , you must specify all the fields you want to include (exceptĀ Ā passwordĀ  ). There is also some additional configuration required for it to integrate with the Django authentication system.Ā 

The key configurations to implement when using the Ā AbstractBaseUserĀ  model are:

  • USERNAME_FIELDĀ  - you must set this to the field you want to use when logging in.

  • EMAIL_FIELDĀ  - set to the field that contains a user's primary email, defaults to Ā 'email'Ā  if not specified.

  • REQUIRED_FIELDSĀ  - set this to any fields that must be specified when using the Ā python manage.py createsuperuserĀ  command.

  • is_activeĀ  -Ā  defaults to Ā TrueĀ  for Ā AbstractBaseUserĀ  , but you can add your own field if you want to handle active and inactive users.Ā 

What if I want to use an email address to log in?Ā 

Easy! Just set the constant Ā USERNAME_FIELDĀ  to the email field. Django requires this to be unique. If you are extending Ā AbstractUserĀ , you can also then remove the Ā usernameĀ fieldĀ  by setting it to Ā NoneĀ  .Ā 

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    email = models.EmailField(unique=True)
    username = None

    USERNAME_FIELD = 'email'

Okay, now that we've gone over some of the different ways to implement a Ā UserĀ  model in Django, let's do it in our app!

Set Up the Django App With a Custom User Model

It is generallyĀ better to build off the AbstractUser model as this will automatically integrate with the rest of the Django frameworkĀ and have the most compatibility with third-party apps.Ā Ā 

We want to include all of the functionality of the default Ā UserĀ  class for our site, so we will extend Ā AbstractUserĀ  .Ā We will also add two additional fields:

  • an Ā ImageFieldĀ  containing a profile photo,

  • and Ā roleĀ , a Ā CharFieldĀ , which will differentiate between two types of users on our site, creators and subscribers.

Step 1: Create the Ā UserĀ  model

AddĀ the Ā UserĀ  to the models in Ā authenticationĀ  .Ā 

# authentication/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    CREATOR = 'CREATOR'
    SUBSCRIBER = 'SUBSCRIBER'
  
    ROLE_CHOICES = (
        (CREATOR, 'Creator'),
        (SUBSCRIBER, 'Subscriber'),
    )
    profile_photo = models.ImageField()
    role = models.CharField(max_length=30, choices=ROLE_CHOICES)

Step 2: Configure Django to Use a Custom Ā User

Out of the box, Django uses the default Ā UserĀ  model for authentication, so you need to tell Django that you want to useĀ yourĀ UserĀ  model instead. To do that, point Ā AUTH_USER_MODELĀ  to the correct model in the settings. When configuring Ā AUTH_USER_MODELĀ  , use the notation Ā '<app-name>.<model-name>'Ā  , giving you:Ā 

# fotoblog/settings.py
AUTH_USER_MODEL = 'authentication.User'

Great, now that you have configured yourĀ UserĀ  model, you can run the initial migrations.Ā 

Step 3: Run the Initial Migrations

Let’s make the migrations first. When doing this, you may run into this error:

(ENV) ~/fotoblog (master)
→ python manage.py makemigrations
SystemCheckError: System check identified some issues:

ERRORS:
authentication.User.profile_photo: (fields.E210) Cannot use ImageField because Pillow is not installed.
	HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".

Django requires the package Ā PillowĀ  in order to use the Ā ImageFieldĀ  . Ā PillowĀ  is a Python library for processing images.Ā 

Versions of Django older than 3.2 don't automatically installĀ PillowĀ . If you see this message, you’ll need to install itĀ using Ā pipĀ  and update theĀ requirements.txtĀ  , then try again.

(ENV) ~/fotoblog (master)
→ pip install Pillow

(ENV) ~/fotoblog (master)
→ pip freeze > requirements.txt 

(ENV) ~/fotoblog (master)
→ python manage.py makemigrations
Migrations for 'authentication':
  authentication/migrations/0001_initial.py
    - Create model User

(ENV) ~/fotoblog (master)
→ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, authentication, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying authentication.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying sessions.0001_initial... OK

Great, you've run the migrations and are set up!

Let's Recap!

  • Django uses theĀ UserĀ model to handle authentication.Ā 

  • It is always a good idea to use a customĀ UserĀ model in a project, even if you don't need added functionality, as it makes it much easier to customize it later.Ā 

  • You can extend theĀ AbstractUserĀ to build on the default Ā UserĀ  model.Ā 

  • You can extendĀ AbstractBaseUserĀ for further flexibility and to design all the fields yourself.

Now thatĀ you can store users in the database let's try to authenticate them on our site.

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best