Reader small image

You're reading from  Django 5 By Example - Fifth Edition

Product typeBook
Published inApr 2024
PublisherPackt
ISBN-139781805125457
Edition5th Edition
Right arrow
Author (1)
Antonio Melé
Antonio Melé
author image
Antonio Melé

Antonio Melé has been crafting Django projects since 2006, for clients spanning multiple industries. He is Engineering Director at Backbase, a leading global fintech firm dedicated to facilitating the digital transformation of financial institutions. He co-founded Nucoro, a digital wealth management platform. In 2009 Antonio founded Zenx IT, a company specialized in developing digital products. He has been working as CTO and consultant for several tech-centric startups. He has also managed development teams building projects for large enterprise clients. He has an MSc in Computer Science from Universidad Pontificia Comillas and completed the Advanced Management Program at MIT Sloan. His father inspired his passion for computers and coding.
Read more about Antonio Melé

Right arrow

Building an API

In the previous chapter, you built a system for student registration and enrollment in courses. You created views to display course contents and learned how to use Django’s cache framework.

In this chapter, you will create a RESTful API for your e-learning platform. An API is a common programmable interface that can be used on multiple platforms like websites, mobile applications, plugins, and so on. For example, you can create an API to be consumed by a mobile application for your e-learning platform. If you provide an API to third parties, they will be able to consume information and operate with your application programmatically. An API allows developers to automate actions on your platform and integrate your service with other applications or online services. You will build a fully featured API for your e-learning platform.

In this chapter, you will:

  • Install Django REST framework
  • Create serializers for your models
  • Build a RESTful...

Functional overview

Figure 15.1 shows a representation of the views and API endpoints that will be built in this chapter:

Figure 15.1: Diagram of API views and endpoints to be built in Chapter 15

In this chapter, you will create two different sets of API views, SubjectViewSet and CourseViewSet. The former will include the list and detail views for subjects. The latter will include the list and detail views for courses. You will also implement the enroll action in CourseViewSet to enroll students in courses. This action will be only available to authenticated users, by using the IsAuthenticated permission. You will create the contents action in CourseViewSet to access a course’s content. To access course contents, users have to be authenticated and enrolled in the given course. You will implement the custom IsEnrolled permission to limit access to contents to the users enrolled in the course.

If you are not familiar with API endpoints, you just need to know...

Building a RESTful API

When building an API, there are several ways you can structure its endpoints and actions, but following REST principles is encouraged.

The REST architecture comes from Representational State Transfer. RESTful APIs are resource-based; your models represent resources, and HTTP methods such as GET, POST, PUT, or DELETE are used to retrieve, create, update, or delete objects. HTTP response codes are also used in this context. Different HTTP response codes are returned to indicate the result of the HTTP request, for example, 2XX response codes for success, 4XX for errors, and so on.

The most common formats to exchange data in RESTful APIs are JSON and XML. You will build a RESTful API with JSON serialization for your project. Your API will provide the following functionalities:

  • Retrieving subjects
  • Retrieving available courses
  • Retrieving course contents
  • Enrolling in a course

You can build an API from scratch with Django...

Consuming the API

By making our views available via URLs, we have created our first API endpoints. Let’s now try our own API. Ensure that your server is running with the following command:

python manage.py runserver

We are going to use curl to consume the API. curl is a command-line tool that allows you to transfer data to and from a server. If you are using Linux, macOS, or Windows 10/11, curl is very likely included in your system. However, you can download curl from https://curl.se/download.html.

Open the shell and retrieve the URL http://127.0.0.1:8000/api/subjects/ with curl, as follows:

curl http://127.0.0.1:8000/api/subjects/

You will get a response similar to the following one:

[
    {
        "id":1,
        "title":"Mathematics",
        "slug":"mathematics"
    },
    {
        "id":2,
        "title":"Music",
        "slug":"music"
    }...

Extending serializers

You have learned how to serialize your model objects; however, often, you may want to enrich the response with additional relevant data or calculated fields. Let’s take a look at some of the options to extend serializers.

Adding additional fields to serializers

Let’s edit the subject views to include the number of courses available for each subject. You will use the Django aggregation functions to annotate the count of related courses for each subject.

Edit the api/views.py file of the courses application and add the following code highlighted in bold:

from django.db.models import Count
# ...
class SubjectListView(generics.ListAPIView):
    queryset = Subject.objects.annotate(total_courses=Count('courses'))
    serializer_class = SubjectSerializer
class SubjectDetailView(generics.RetrieveAPIView):
    queryset = Subject.objects.annotate(total_courses=Count('courses'))
    serializer_class = SubjectSerializer
...

Adding pagination to views

DRF includes built-in pagination capabilities to control how many objects are sent over in your API responses. When the content of your site starts to grow, you might end up with a large number of subjects and courses. Pagination can be particularly useful to improve performance and the user experience when dealing with large datasets.

Let’s update the SubjectListView view to include pagination. First, we will define a pagination class.

Create a new file inside the courses/api/ directory and name it pagination.py. Add the following code to it:

from rest_framework.pagination import PageNumberPagination
class StandardPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 50

In this class, we inherit from PageNumberPagination. This class provides support for pagination based on page numbers. We set the following attributes:

  • page_size: Determines the default...

Building the course serializer

We are going to create a serializer for the Course model. Edit the api/serializers.py file of the courses application and add the following code highlighted in bold:

# ...
from courses.models import Course, Subject
class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = [
            'id',
            'subject',
            'title',
            'slug',
            'overview',
            'created',
            'owner',
            'modules'
        ]

Let’s take a look at how a Course object is serialized. Open the shell and execute the following command:

python manage.py shell

Run the following code:

>>> from rest_framework.renderers import JSONRenderer
>>> from courses.models import Course
>>> from courses.api.serializers import CourseSerializer
>>> course...

Creating nested serializers

If we want to include more information about each module, we need to serialize Module objects and nest them. Modify the previous code of the api/serializers.py file of the courses application to make it look as follows:

from django.db.models import Count
from rest_framework import serializers
from courses.models import Course, Module, Subject
class ModuleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Module
        fields = ['order', 'title', 'description']
class CourseSerializer(serializers.ModelSerializer):
    modules = ModuleSerializer(many=True, read_only=True)
    class Meta:
        # ...

In the new code, you define ModuleSerializer to provide serialization for the Module model. Then, you modify the modules attribute of CourseSerializer to nest the ModuleSerializer serializer. You keep many=True to indicate that you are serializing multiple objects and read_only=True to keep this field...

Creating ViewSets and routers

ViewSets allow you to define the interactions of your API and let DRF build URLs dynamically with a Router object. By using ViewSets, you can avoid repeating logic for multiple views. ViewSets include actions for the following standard operations:

  • Create operation: create()
  • Retrieve operation: list() and retrieve()
  • Update operation: update() and partial_update()
  • Delete operation: destroy()

Let’s create a ViewSet for the Course model. Edit the api/views.py file and add the following code highlighted in bold:

from django.db.models import Count
from rest_framework import generics
from rest_framework import viewsets
from courses.api.pagination import StandardPagination
from courses.api.serializers import CourseSerializer, SubjectSerializer
from courses.models import Course, Subject
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.prefetch_related('modules')
    serializer_class...

Building custom API views

DRF provides an APIView class that builds API functionality on top of Django’s View class. The APIView class differs from View by using DRF’s custom Request and Response objects and handling APIException exceptions to return the appropriate HTTP responses. It also has a built-in authentication and authorization system to manage access to views.

You are going to create a view for users to enroll in courses. Edit the api/views.py file of the courses application and add the following code highlighted in bold:

from django.db.models import Count
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from courses.api.pagination import StandardPagination
from courses.api.serializers import CourseSerializer, SubjectSerializer
from courses.models import Course, Subject
# ...
class CourseEnrollView...

Handling authentication

DRF provides authentication classes to identify the user performing the request. If authentication is successful, the framework sets the authenticated User object in request.user. If no user is authenticated, an instance of Django’s AnonymousUser is set instead.

DRF provides the following authentication backends:

  • BasicAuthentication: This is HTTP basic authentication. The user and password are sent by the client in the Authorization HTTP header, encoded with Base64. You can learn more about it at https://en.wikipedia.org/wiki/Basic_access_authentication.
  • TokenAuthentication: This is token-based authentication. A Token model is used to store user tokens. Users include the token in the Authorization HTTP header for authentication.
  • SessionAuthentication: This uses Django’s session backend for authentication. This backend is useful for performing authenticated AJAX requests to the API from your website’s frontend...

Adding permissions to views

DRF includes a permission system to restrict access to views. Some of the built-in permissions of DRF are:

  • AllowAny: Unrestricted access, regardless of whether a user is authenticated or not.
  • IsAuthenticated: Allows access to authenticated users only.
  • IsAuthenticatedOrReadOnly: Complete access to authenticated users. Anonymous users are only allowed to execute read methods such as GET, HEAD, or OPTIONS.
  • DjangoModelPermissions: Permissions tied to django.contrib.auth. The view requires a queryset attribute. Only authenticated users with model permissions assigned are granted permission.
  • DjangoObjectPermissions: Django permissions on a per-object basis.

If users are denied permission, they will usually get one of the following HTTP error codes:

  • HTTP 401: Unauthorized
  • HTTP 403: Permission denied

You can read more information about permissions at https://www.django-rest-framework.org/api...

Adding additional actions to ViewSets

You can add extra actions to ViewSets. Let’s change the CourseEnrollView view into a custom ViewSet action. Edit the api/views.py file and modify the CourseViewSet class to look as follows:

# ...
from rest_framework.decorators import action
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.prefetch_related('modules')
    serializer_class = CourseSerializer
    @action(
        detail=True,
        methods=['post'],
        authentication_classes=[BasicAuthentication],
        permission_classes=[IsAuthenticated]
    )
    def enroll(self, request, *args, **kwargs):
        course = self.get_object()
        course.students.add(request.user)
        return Response({'enrolled': True})

In the preceding code, you add a custom enroll() method that represents an additional action for this ViewSet. The preceding code is as follows:

  1. You use the action decorator of...

Creating custom permissions

You want students to be able to access the contents of the courses they are enrolled on. Only students enrolled on a course should be able to access its contents. The best way to do this is with a custom permission class. DRF provides a BasePermission class that allows you to define the following methods:

  • has_permission(): A view-level permission check
  • has_object_permission(): An instance-level permission check

These methods should return True to grant access, or False otherwise.

Create a new file inside the courses/api/ directory and name it permissions.py. Add the following code to it:

from rest_framework.permissions import BasePermission
class IsEnrolled(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.students.filter(id=request.user.id).exists()

You subclass the BasePermission class and override the has_object_permission(). You check that the user performing the request...

Serializing course contents

You need to serialize course contents. The Content model includes a generic foreign key that allows you to associate objects of different content models. Yet, you added a common render() method for all content models in the previous chapter. You can use this method to provide rendered content to your API.

Edit the api/serializers.py file of the courses application and add the following code to it:

from courses.models import Content, Course, Module, Subject
class ItemRelatedField(serializers.RelatedField):
    def to_representation(self, value):
        return value.render()
class ContentSerializer(serializers.ModelSerializer):
    item = ItemRelatedField(read_only=True)
    class Meta:
        model = Content
        fields = ['order', 'item']

In this code, you define a custom field by subclassing the RelatedField serializer field provided by DRF and overriding the to_representation() method. You define the ContentSerializer...

Consuming the RESTful API

Now that you have implemented an API, you can consume it in a programmatic manner from other applications. You can interact with the API using the JavaScript Fetch API in the frontend of your application, in a similar fashion to the functionalities you built in Chapter 6, Sharing Content on Your Website. You can also consume the API from applications built with Python or any other programming language.

You are going to create a simple Python application that uses the RESTful API to retrieve all available courses and then enroll a student in all of them. You will learn how to authenticate against the API using HTTP basic authentication and perform GET and POST requests.

We will use the Python Requests library to consume the API. We used Requests in Chapter 6, Sharing Content on Your Website, to retrieve images by their URL. Requests abstracts the complexity of dealing with HTTP requests and provides a very simple interface to consume HTTP services...

Summary

In this chapter, you learned how to use DRF to build a RESTful API for your project. You created serializers and views for models, and you built custom API views. You also added authentication to your API and restricted access to API views using permissions. Next, you discovered how to create custom permissions, and you implemented ViewSets and routers. Finally, you used the Requests library to consume the API from an external Python script.

The next chapter will teach you how to build a chat server using Django Channels. You will implement asynchronous communication using WebSockets, and you will use Redis to set up a channel layer.

Additional resources

The following resources provide additional information related to the topics covered in this chapter:

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Django 5 By Example - Fifth Edition
Published in: Apr 2024Publisher: PacktISBN-13: 9781805125457
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Antonio Melé

Antonio Melé has been crafting Django projects since 2006, for clients spanning multiple industries. He is Engineering Director at Backbase, a leading global fintech firm dedicated to facilitating the digital transformation of financial institutions. He co-founded Nucoro, a digital wealth management platform. In 2009 Antonio founded Zenx IT, a company specialized in developing digital products. He has been working as CTO and consultant for several tech-centric startups. He has also managed development teams building projects for large enterprise clients. He has an MSc in Computer Science from Universidad Pontificia Comillas and completed the Advanced Management Program at MIT Sloan. His father inspired his passion for computers and coding.
Read more about Antonio Melé