Skip to main content

User Setup and Migrations

Authentication with Djoser

Django REST Framework provides basic authentication out of the box, provided with the defaults located in INSTALLED_APPS under settings.py

To build robust applications however, you will need to more functionality than what it offers;

  • Registration
  • Account Activation (with activation codes sent via email)
  • Password Resets (also sent via email)
  • Session expiry with JSON Web Tokens (so that you don't just stay logged in forever)
  • and so on...

These features are not built in to DRF, and so we'll have to bring in another package or library for this, Djoser

To start, run the command pipenv install djoser djangorestframework-simplejwt

image.png Add djoser and rest_framework_simplejwt to INSTALLED_APPS under settings.py

image.png

You should also add the following code blocks to the end of your settings.py.

REST_FRAMEWORK will change the authentication type

DJOSER provides settings that you will use later on for user registration

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}

DJOSER = {
'SEND_ACTIVATION_EMAIL': True,
'SEND_CONFIRMATION_EMAIL': True,
'ACTIVATION_URL': 'activation/{uid}/{token}',
}

image.png

Add the following to your url_patterns in urls.py file under the api app

path('', include('djoser.urls')),
path('', include('djoser.urls.jwt'))

image.png

Next step, models!

Note!

If you've gotten this far, congratulations! If you're not familiar with basic and intermediate SQL or Python exercises yet, you may have trouble catching up in the following sections. Please consider learning those first or alongside the sections below.

User Model

We've added authentication (Djoser) and so we will need to create our own concept of a user.

Models are the heart of backend development (not just Django!). While you may know what a car or a person is, your backend (Django) and your database have no idea how this should be structured.

A person can have the following

  • Name
  • Age
  • Birthday

Cars can have them too! But you get my point.

Django already provides a default User model, hidden away from sight. We will be overriding and replacing this with our own.

To start, we will need to create a separate app.

In the previous section, we created a simple folder for the api app, this will not work for this step.

Instead, make sure you're inside the Django project directory by doing cd PROJECT_NAME, in my case, cd djangobackend

We will then create an accounts app. Run the command python manage.py startapp accounts image.png This will create the accounts app

Note!

The best practice when creating Django apps would be naming them in plural form (Books, Records, Posts)

The Books app can hold

  • Books
  • Pages
  • Chapters

Because of this, there is usually no need to make another app for pages and chapters as mentioned above, unless your app is big enough.

With that out of the way, let's begin creating our User model.

We will be creating our own version of a User based on the default one provided by Django herewith an age and a birthday

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

class CustomUser(AbstractUser):
# Some fields are referenced or copied over from AbstractUser
# first_name, last_name, email, username, and password are among a few of these

birthday = models.DateTimeField(null=True)
age = models.IntegerField(null=True)

@property
def full_name(self):
return f"{self.first_name} {self.last_name}"

If we CTRL + Click on AbstractUser, we can take a deeper look under the hood on Django's default User image.png

By referencing AbstractUser on the User we're creating, we're can skip over creating the other fields a user would have (password, email) and the work that comes with how those work under the hood. This is something you will regularly see with Django and will make developing projects faster.

We'll now need to connect our accounts app and the User model to our existing project

Open the admin.py file in the accounts app and add the following code block.

from django.contrib import admin
from .models import CustomUser

# Register your models here.
admin.site.register(CustomUser)

image.png

This will register the model you just created into the Django admin panel (which you will look into later on).

You should then head over to your settings.py under the config app and add accounts to INSTALLED_APPS image.png

Point your authentication to your new User model by adding this to settings.py under the config app

AUTH_USER_MODEL = 'accounts.CustomUser'

image.png

Next, you will need to shuffle some things around

Head back to the accounts app and create a urls.py file. Add the following code block

from django.urls import path, include

urlpatterns = [
path('', include('djoser.urls')),
path('', include('djoser.urls.jwt')),
]

You might notice that this is similar to what we have on the api app, we'll be moving these entries to here.

Now that we have those moved over, remove the entries for djoser.urls and djoser.urls.jwt in urls.py under the api app

from django.contrib import admin
from django.urls import path, include

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

Instead, replace what we've removed with

path('accounts/', include('accounts.urls')),

Things should look like this after

urls.py under api app image.png urls.py under accounts app image.png

With this, your new accounts app is now connected.

Migrations

Django and Django REST Framework (DRF) serves as an interface between your database (MySQL, PostgreSQL, SQLite) and the internet.

It DOES NOT hold your data. Rather, it serves as the backend to access said data.

image.png

This is an important distinction to make especially for starters. Exposing your database without a framework such as Django can allow malicious actors to easily make a mess of everything. Everything Django and DRF provides (authentication, middleware, etc.) serves this purpose.

Carrying on, migrations!

Migrations keep track of what types of kinds of things we wish to store. You previously created a User model (accounts app). image.png To apply these changes to your database, run the command python manage.py makemigrations

Note!

Make sure you're inside the Django project directory! (e.g. cd PROJECT\_NAME) image.png This will create a migration file for your User model, which will instruct Django on how to create it inside your SQL database.

Taking a closer look at the newly created migrations file, you can see the following file in accounts/migrations/0001\_initial.py image.png

The previous command you just ran (python manage.py makemigrations) just translated your User model (located in accounts/models.py) into instructions which will then be used to create the SQL statements under the hood (e.g. CREATE TABLE USER (username char, password ...))

Migrations are very analogous to sandwiches and burgers in a sense, if you decide to add a grumpy attribute to your User image.png

and run python manage.py makemigrations again, image.png

This will create a second migration file (0002\_customuser\_grumpy.py) image.png

When you apply these migrations (which we will do shortly), Django reads each migration file in order, starting off with #1 to create the initial User model, and then to step #2 to add the grumpy field, which under the hood uses SQL Alter (e.g. ALTER USER ADD grumpy boolean).

Django's migrations will also let you know if you make changes to your models that might break things (e.g. removing an already added field when you have records for that already).

This is something you should ideally understand with Django and how it interfaces with your database under the hood.

Apply Migrations

If you've noticed this error in the previous sections, this is because we haven't applied our migrations yet! image.png With your user schema or template now set up, you should now apply these changes to the database.

Run the command python manage.py migrate to apply these changes image.png

This will apply your changes to the database, which by default is SQLite**,** the db.sqlite3 file.

With that out of the way, running your Django project (python manage.py runserver 0.0.0.0:8000) will no longer yield the migration warning. image.png

It's always important to apply your migrations before running your app, otherwise you might run into issues.

In the next section, you will be creating your first (superuser) account to access the Django admin panel.

Up Next: 6 - Django Admin