
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.txtOur 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 blogAdd 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 commitThe project is now set up. Next , let's configure individual users in Django.
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Ā .
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
AbstractUserTheĀ 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):
passThisĀ 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.Ā
AbstractBaseUserTheĀ 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!
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.
UserĀ modelAddĀ 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)UserOut 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.Ā
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... OKGreat, you've run the migrations and are set up!
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.