| Josue Kula

Aug
22

Restful API in DJango 3.x

How to write URL endpoints with generics views

By josue kula @J_kuler

Applications running on the web are rarely isolated from each other. The internet serves not only static pages but also allows applications to share data using web services. REST (Representational state transfer) as an architectural style for creating web service remains the most implemented architecture by organizations to deployed web services between dynamic applications.

To create web services in python, developers leverage the Django framework’s utilities and tools to speed up the process of implementing business functions as electronic services—Create records, compute numbers, and update persistent data. Nonetheless, writing efficient web service endpoints requires a clear understanding of what Django (Django rest framework) offers to create a fully Restful API. And developers need to know how to use it in different scenarios. Failing to grasp functionalities of the API—Django, in this case, would lead to a lot of inconsistency in terms of data retrieval, maintainability of the applications ultimately, failure to meet business functions. The following figure depicts how Django and the djangorestframework interact to handles requests and responses.

The first thing to retain from figure 1.0 is that Django is a web framework and djangorestframework is an application that extends Django views, URL configuration, models, and other classes to serialize data in different content types before writing or reading data from the database.

Second, the views classes like the generics or view set always take precedence over serializers when handling incoming requests. The serializers packages or classes are closer to the models instead of the templates or the URL dispatcher.

Lastly, when the client issues a request, the URL dispatcher validates a URL against URL configuration. If it is valid, it passes the request to the views classes that handle respective HTTP verbs if one exists. Next, the serializers take control to validate data which goes down to the model for the final validation before hitting the database. Finally, the response goes upward from the models’ classes to the serializers, the views, and the client.

Before Creating A REST API

We create Restful API resources to be consumed by other applications. Thus, before writing resources endpoints, we need to design URLs, define data and the content type that other applications or web services will need to achieve businesses objectives. Bear in mind that we exclude the RDBMS configuration that is not subject to the scope of this blog. However, we’ll look at a simple data model in Django that djangorestframework would require to expose data via a valid HTTP resource endpoint.

Data Model

As mentioned above, the sole purpose of a web service is to communicate data between applications. So, we need to define information (not data) that the restful API will read and write to the database via a URL endpoint. Here is a simple model class that defines properties of the entity called payment—Supposed a business wants to record customer payments.

                                
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User


def auto_datetime():
    return timezone.now().strftime('%Y-%m-%dT%H:%M:%S')


class Payment(models.Model):
    paid_on = models.DateTimeField(default=auto_datetime(),
                                   editable=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE,
                                related_name='payments')
    amount = models.FloatField()

    class Meta:
        ordering = ['id']

   def __str__(self):
         return self.id
                           

There is a lot to unpack about this model. But one thing we need to point out is the usage of `timezone.now` instead of using auto_now_add as a parameter on the paid_on field. In this case, we enforce the Datetime string's format for the paid_on's attribute—This will make it easier to query DateTime as a query parameter.

Serialization

Although Django automatically generates the models.py for us during the app creation, it does create the serializers.py that djangorestframework needs to bind data between our views and the model class. So, we must make a serializers.py file that handles the serialization of all incoming and outgoing data.

                                    
__author__ = "Josue Kula"
__copyright__ = "Copyright (c) 2021 josuekula.com"

from rest_framework import fields
from rest_framework.serializers import ModelSerializer
from .models import Payment


class CustomerPaymentSerializer(ModelSerializer):
    paid_on = fields.DateTimeField(format="%Y-%m-%dT%H:%M:%S",
                                   read_only=True)

    class Meta:
        model = Payment
        fields = ['id', 'user', 'paid_on', 'amount']

    def validate(self, attrs):
        instance = Payment(**attrs)
        instance.clean()
        return attrs
                                

As you see, we have a simple serializer class that extends ModelSerializer with a paid_on field which should be read-only and keeps the same DateTime format from the database.

Resquest Handler: Views Class

If you have used hapi.js before or similar javascript Restful frameworks, you might be familiar with the famous handler function that controls all HTTP requests and responses on a given endpoint. In Django, the file views.py defines a class that extends other `djangorestframework` classes to create views.

There are different ways to write views in Django; one can use a simple function or a class-based View. It's worth noting here that we are using the class ListCreateAPIView from the `restframework.generics` package instead of APIView, ViewSet, or ModelViewSet. In urls.py, we have called an undefined class view. So, let's create the actual class that will handle all incoming requests.

                                    
__author__ = "Josue Kula"
__copyright__ = "Copyright (c) 2021 josuekula.com"

from rest_framework.generics import ListCreateAPIView
from rest_framework.response import Response
from rest_framework import status
from django.core.exceptions import FieldError
from .models import Payment
from .serializers import CustomerPaymentSerializer


class CustomerPayments(ListCreateAPIView):
    """ Retrieve and create payment data model.
        Filter data with valid attributes as query parameters.
        :returns an Array of payments, single payment object
        or 404 not found message.
    """
    serializer_class = CustomerPaymentSerializer

    def get_queryset(self):
        payments_qs = Payment.objects.all()
        # Generates a new dictionary from query_params dict()
        # if query param key is not empty.
        filtering_dict = dict([qs for qs in self.request.query_params.items()
                              if qs[1] != ''])
        try:
            # filter QuerySet[] with valid query parameters.
            payments = payments_qs.filter(**filtering_dict)
        # Catch undefined field passed as query parameter
        except FieldError:
            return []
        return payments

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = CustomerPaymentSerializer(queryset, many=True,
                                               context={'request': request})
        # modify an empty response to
        if not serializer.data:
            return Response({"result": f"No record found."},
                            status=status.HTTP_404_NOT_FOUND)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def post(self, request, *args, **kwargs):
        serializer = CustomerPaymentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

                                

The first thing to mention about our view is that it only Implements two main business functions—Information retrieval and creation. That being the case, our exposed endpoint can handle GET (HEAD) and POST without considering another idempotent verb like OPTIONS. Second, our view class does not require authentication to create or retrieve information from the database.

Lastly, we have the `get_queryset()` that handles Queryset filtering before passing data to the serializer for validation then onto the response object.

Contributions

Djangorestframework has been for many the technology of choice (under python) for creating a restful API as quickly as possible, but requirements sometimes get out of control. Therefore, developers should have a better understanding of REST architecture and object-oriented concepts to deal with complexity.

In this blog, we saw how the ListCreateAPIView from the generics package facilitates writing views to avoid unnecessary repetitions by abstracting the function that handles Queryset from HTTP method handlers—get() or post(). The good news from the generics is that you don’t have to extend the ModelSerializer class on your serializer class.

About Josue

Josue Kula is a software engineer specialized in developing custom software, websites, and mobile apps while prioritizing security and user experience.

His entrepreneurial drive fuels his passion for leveraging technology to drive business growth through innovative programming solutions.

Contact

  • Josue Kula
  • New York, NY
  • josue.kula@outlook.com

Designed by Josue Kula | Copyright © 2018 - 2025 josuekula.com