Web Application using Django. Part 2
By: Himraj Gogoi
Tags: Django Framework, Django, Backend Development, Authentication, Django Rest Framework

In part 1 of this series, we saw how to create a Django project, how to create an app within the project, model for the app, serializers for the model, API view for the serializers to have CRUD operations on the model, creating API Endpoints for the API view and finally creating and connecting the urls of the app with the project urls.

If you have not read part 1 of this 3-part series, visit the link,

In part 2 of the series, we will see how we can add authentication to our application (back-end section).

Let’s begin,

Part 1. (Making messages authenticated)

Within the messages folder, in the models.py file, add:

from django.contrib.auth.models import User

Django provides us with a ready-made User model which contains all the necessary fields, username, email, password and others. We can even customize this User model according to our need as and when required. Check out this article for customizing the user model,

Once imported, inside the Message class, add:

owner = models.ForeignKey(User, related_name= 'messages', on_delete=models.CASCADE, null= True)

Here we are creating the owner field to represent the user who created the message.

Make necessary migrations to reflect the changes.

2. Then, in the api.py file, modify the code as:

from messages.models import Message from rest_framework import viewsets, permissions from .serializers import MessageSerializer # Message Viewset class MessageViewSet(viewsets.ModelViewSet): permission_classes = [ permissions.IsAuthenticated ] serializer_class = MessageSerializer

def get_queryset(self): return self.request.user.messages.all() def perform_create(self, serializer): serializer.save(owner = self.request.user)

Here we have modified the permission classes to allow only authenticated users and defined the get_queryset method to return the messages of only the logged in user.

Also we defined a perform_create function method to add the owner whenever a user creates a message.

Part 2. (User Sign-Up/Sign-In and Logout functionality)

Stage 1.

Within the messagemanager folder, in the settings.py file, under INSTALLED_APPS add “knox”. Then, in the same settings.py file, add:

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':('knox.auth.TokenAuthentication',) }

Django provides us with it’s own authentication classes, but we will be using the authentication provided by django-rest-knox.

** remember, to run commands within a django project, we must enable the virtual environment, so in the command prompt, within the root folder, always run:

pipenv shell

Then proceed.

3. Then, in the Command Prompt, within the project folder, run:

python manage.py migrate

This will make the necessary migrations.

4. Again, create an app “accounts”, as:

python manage.py startapp accounts

Add the accounts app under the INSTALLED_APPS section in the settings.py file.

Stage 2.

Register API

Open the root folder in any code editor (VS Code, Atom or any other) and within the accounts folder, create a serializers.py file and add:

from rest_framework import serializers from django.contrib.auth.models import User from django.contrib.auth import authenticate #User Serializer class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields =("id", "username", "email") #Register Serializer class RegisterSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('id', 'username', 'email', 'password') extra_kwargs = {'password': {'write_only': True}} def create(self, validated_data): user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password']) return user

Here we are creating two serializers- UserSerializer and RegisterSerializer.

We are using the User model provided by the models module from django.contrib.auth .

In the UserSerializer class, under Meta class, we specify the model and the fields we want to have.

In the RegisterSerializer class, under Meta class, we specify the model and the fields we have. Also, we take extra keyword arguments to provide specific instructions.

Then we define the create method explicitly to create a user using validated_data. The create method is called whenever a new user registers and the validated data makes sure there is no duplication of username and email.

Next, create an api.py file and add:

from rest_framework import generics, permissions from rest_framework.response import Response from knox.models import AuthToken from .serializers import UserSerializer, RegisterSerializer # Register API class RegisterAPI(generics.GenericAPIView): serializer_class = RegisterSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception= True) user = serializer.save() return Response({ 'user': UserSerializer(user, context=self.get_serializer_context()).data, 'token': AuthToken.objects.create(user)[1] })

It might seem a lot of code, but trust me, once you get the workflow its very easy to understand :-) .

So we have imported certain classes and the serializers of course. Next, we have defined the RegisterAPI which will work with the Register API endpoint later on. Take note of the GenericAPIView. This class extends REST framework’s APIView class, adding commonly required behavior for standard list and detail views.( Check out the documentation for API reference of Django Rest Framework.) Then, we have explicitly defined the post method in which we have serialized the request data, checked if it is valid and finally created a user instance in the User model.

At the end we have provided a response, which will return the registered user’s username and email and a token, generated by the AuthToken class of knox.models .

Finally, create a urls.py file and add:

from django.urls import path, include from .api import RegisterAPI from knox import views as knox_views urlpatterns = [ path('api/auth', include('knox.urls')), path('api/auth/register', RegisterAPI.as_view()), ]

Here, before defining the register API endpoint, we have defined the /api/auth endpoint to enable the authentication functionalities provided by knox. Also, since we are using generic API views, we must invoke the as_view() method onto our RegisterAPI. (I’ll explain in a later post in detail, why we use as_view() )

And then, include the accounts.urls in messagemanager folder → urls.py under urlpatterns.

2. Login API

Within the accounts folder, in the serializers.py file, add:

#Login Serializer class LoginSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField() def validate(self, data): user = authenticate(**data) if user and user.is_active: return user raise serializers.ValidationError("Incorrect credentials")

Here we are defining a login serializer which takes two serialized fields, username and password. Then, the validate method authenticates the data provided by the user. If the user is valid, the method returns a user, otherwise raises an error.

Then, in the api.py file, add:

# Login API class LoginAPI(generics.GenericAPIView): serializer_class = LoginSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception= True) user = serializer.validated_data return Response({ 'user': UserSerializer(user, context=self.get_serializer_context()).data, 'token': AuthToken.objects.create(user)[1] })

The LoginAPI class is almost identical to the RegisterAPI class, the only difference being in case of LoginAPI class, we fetch the validated user, rather than creating a new user. See the post method, here we have assigned the validated user to the user variable.

In the urls.py, import the LoginAPI class and under the urlpatterns, add:

path('api/auth/login', LoginAPI.as_view()),

3. Logged In user data

Within the accounts folder, in the api.py file, add:

# Get User API class UserAPI(generics.RetrieveAPIView): permission_classes = [ permissions.IsAuthenticated, ] serializer_class = UserSerializer def get_object(self): return self.request.user

Here, we are fetching the logged In user’s data using the get_object method in the UserAPI class.

Quick note → Django Rest Framework provides us with a number of APIViews and even combinations of the same. The class above uses the RetrieveAPIView which enables us to fetch only a single instance of a model, in this case it is the logged-in user’s data.

Finally, in the urls.py file, import the class and add the following under urlpatterns:

path('api/auth/user', UserAPI.as_view()),

4. Logout API

In the urls.py file, under urlpatterns, add:

path('api/auth/logout', knox_views.LogoutView.as_view(), name="knox_logout")

knox provides us with its own set of authentication facilities and we are leveraging the LogoutView to logout the user.

Finally, to run the server, in the Command Prompt, within the project folder, run:

python manage.py runserver

If everything goes well, you will get the port where the server has been hosted, i.e. the localhost.