Skip to main content

Django Rest Framework

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. It is a third-party package for Django, the popular Python web framework. DRF makes it easy to build, test, and debug RESTful APIs written using the Django framework.

DRF provides a simple way to create RESTful APIs for your Django applications. It has a lot of built-in functionality that makes it easy to get started with building APIs.

Once you have DRF installed and added to your INSTALLED_APPS, you can start using it in your Django views.

Understanding Requests and Responses

Django Rest Framework (DRF) allows you to interact with the API by sending HTTP requests and receiving HTTP responses. Understanding requests and responses is an important concept for building efficient and effective APIs using DRF.

Here is a beginner-friendly introduction for understanding requests and responses in DRF:

Requests

In DRF, a request is an HTTP message that is sent to the server by a client. It contains information such as the HTTP method (e.g. GET, POST, PUT, PATCH, DELETE), the URL of the resource to be accessed, and any data or parameters that need to be passed to the server.

HTTP Methods

DRF supports four main HTTP methods, each of which is used for different purposes:

  • GET: retrieves information from the server
  • POST: creates a new resource on the server
  • PUT: updates an existing resource on the server
  • PATCH: updates an existing resource field on the server
  • DELETE: deletes a resource from the server

URL Patterns

In DRF, each resource is identified by a unique URL. URL patterns are used to match the URLs of incoming requests with the appropriate views or view functions that should handle them.

Responses

A response is the data that the server sends back to the client in response to a request. Responses in DRF are typically in the form of JSON or XML data, although other formats such as HTML or plain text can be used as well.

Status Codes

Responses in DRF include a status code that indicates the outcome of the request. Some common status codes include:

  • 200 OK: The request was successful and the data was returned
  • 201 Created: A new resource was created as a result of the request
  • 204 No Content: The request was successful but no data was returned
  • 400 Bad Request: The request was invalid or incomplete
  • 401 Unauthorized: The client was not authorized to make the request
  • 404 Not Found: The requested resource was not found on the server
  • 500 Internal Server Error: An error occurred on the server

Understanding these basic concepts is crucial for working with DRF and building efficient and effective APIs.

Serializers and Deserializers

Serializers and Deserializers are the core components in Django REST framework (DRF) that allow you to convert complex data types such as Django models into Python data types that can be easily rendered into JSON, XML or other content types.

Serializers

Serializers are used to convert Django models into Python data types that can be easily serialized into JSON or other content types. In DRF, serializers are used to define the structure of the output data by defining the fields that should be included in the output.

To use serializers in DRF, you first need to create a class that inherits from the serializers.Serializer class. You then need to specify the fields that you want to include in the output by adding class level variables.

For example, consider a model called Manager that has fields for name, country and no_of_titles:

from rest_framework import serializers

class ManagerSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
country = serializers.CharField(max_length=100)
no_of_titles = serializers.IntegerField()

class Meta:
model = Manager
fields = "__all__"

In this example, the ManagerSerializer class is defined to include three fields, name, country and no_of_titles, each of which is specified as a CharField or IntegerField.

Deserializers

Deserializers are used to convert incoming data into Django models or Python objects. In DRF, deserializers are used to validate and parse the incoming data, and to create or update instances of Django models based on the input data.

To use deserializers in DRF, you need to create a class that inherits from the serializers.Deserializer class. You then need to specify the fields that should be included in the input data, and the types of each of these fields, by adding class level variables.

For example, consider the same Manager model as in the previous example:

from rest_framework import serializers

class ManagerDeserializer(serializers.Deserializer):
name = serializers.CharField(max_length=100)
country = serializers.CharField(max_length=100)
no_of_titles = serializers.IntegerField()

In this example, the ManagerDeserializer class is defined to include the same three fields as in the ManagerSerializer class, name, country and no_of_titles.

To use the deserializer, you can pass the incoming data to its .save() method, which will validate and parse the data, and return an instance of the Django model.

manager_data = {'name': 'Jurgen Klopp', 'country': 'Germany', 'no_of_titles': 5}
manager = ManagerDeserializer(data=manager_data).save()

In this example, the manager_data dictionary is passed to the ManagerDeserializer, which validates and parses the data and returns an instance of the Manager model.

Viewsets and Routers

Django REST framework (DRF) provides a powerful and flexible framework for building APIs. It includes features such as authentication, serialization, query parameters, pagination, and more. One of the key features of DRF is Viewsets and Routers, which allow you to quickly build RESTful APIs with minimal code.

In DRF, a ViewSet is a class-based view that provides a set of methods to handle incoming HTTP requests. The ViewSet class should inherit from a base ViewSet class provided by DRF, such as viewsets.ModelViewSet, viewsets.ReadOnlyModelViewSet, or viewsets.GenericViewSet. The base ViewSet class defines the basic methods for handling HTTP requests, such as list(), create(), retrieve(), update(), and destroy().

To use a ViewSet, you will typically also need to define a serializer class to control how your models are represented in the API. A serializer class defines the fields to be included in the API, how to validate incoming data, and how to deserialize incoming data into model instances.

DRF also provides a Router class that makes it easy to map ViewSets to URL patterns. A Router can be created using the DefaultRouter or SimpleRouter class, and it automatically generates the URL patterns for your ViewSets based on their methods. You can then include the URL patterns in your Django URL configuration, and your ViewSets will be ready to handle incoming requests.

Here is a simple example of how you might use a ViewSet and Router in DRF:

from rest_framework import viewsets
from rest_framework.routers import DefaultRouter
from .models import Manager
from .serializers import ManagerSerializer


class ManagerViewSet(viewsets.ModelViewSet):
queryset = Manager.objects.all()
serializer_class = ManagerSerializer


router = DefaultRouter()
router.register(r'managers', ManagerViewSet)

In this example, we create a ViewSet class called ManagerViewSet, which inherits from viewsets.ModelViewSet. We also define a queryset and serializer_class property, which will be used by DRF to fetch the data and serialize the data for our API.

Next, we create a DefaultRouter instance and register our ViewSet with it. The register() method maps the ViewSet to a URL pattern and adds it to the router's URL patterns. In this case, the URL pattern will be /api/managers/.

Finally, you would need to include the URL patterns in your Django URL configuration:

from django.urls import path, include

urlpatterns = [
path('api/', include(router.urls)),
]

With this setup, DRF will automatically generate the appropriate views and URL patterns for our Manager model, and we can start using the API to fetch, create, update, and delete Manager instances.

This is just a high-level overview of how ViewSets and Routers work in DRF, but you can learn more about these features in the DRF documentation.

Customizing responses using views and viewsets

Django Rest Framework (DRF) provides a lot of built-in features to customize the response of the API endpoints. One way to customize the responses is by using views and viewsets.

A view in DRF is a simple Python function that takes in a web request and returns a web response. A viewset, on the other hand, is a class that defines the behavior of a set of views.

Let's create a simple view to customize the response of an API endpoint.

from rest_framework import views
from rest_framework.response import Response

class CustomResponseView(views.APIView):
def get(self, request):
data = {
'message': 'Hello, World!'
}
return Response(data)

In the above code, we created a custom view CustomResponseView that extends APIView. This view has a simple get method that takes in the request and returns a custom response in the form of a Response object.

Similarly, we can also customize the response of an API endpoint using a viewset.

from rest_framework import viewsets
from rest_framework.response import Response

class CustomResponseViewSet(viewsets.ViewSet):
def list(self, request):
data = {
'message': 'Hello, World!'
}
return Response(data)

In the above code, we created a custom viewset CustomResponseViewSet that extends ViewSet. This viewset has a simple list method that takes in the request and returns a custom response in the form of a Response object.

tip

The list method corresponds to the GET request of an API endpoint. Similarly, you can define other methods like create, retrieve, update, and destroy for other HTTP methods like POST, GET, PUT, PATCH, and DELETE respectively.

With these simple examples, you can start customizing the response of your API endpoints using views and viewsets in DRF.

Working with relationships and nested serializers

Django Rest Framework (DRF) supports working with relationships and nested serializers. A relationship can be defined as one-to-many, many-to-one or many-to-many. Nested serializers allow to represent a model using other models as fields.

Create your Django models

class Club(models.Model):
name = models.CharField(max_length=100)
nick_name = models.CharField(max_length=100)
division = models.CharField(max_length=100)
country = models.CharField(max_length=100)

class Coach(models.Model):
name = models.CharField(max_length=100)
no_of_titles = models.IntegerField(default=0)
club = models.ForeignKey(Club, on_delete=models.SET_NULL)

In the above example, the Club model has a one-to-many relationship with the Coach model.

Create serializers for your models

class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = '__all__'

class CoachSerializer(serializers.ModelSerializer):
club = ClubSerializer(many=False, read_only=True)

class Meta:
model = Coach
fields = '__all__'

Create views for your models

class ClubList(generics.ListCreateAPIView):
queryset = Club.objects.all()
serializer_class = ClubSerializer

class ClubDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Club.objects.all()
serializer_class = ClubSerializer

class CoachList(generics.ListCreateAPIView):
queryset = Coach.objects.all()
serializer_class = CoachSerializer

class CoachDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Coach.objects.all()
serializer_class = CoachSerializer

Add URLs for your views

from django.urls import path

urlpatterns = [
path('clubs/', ClubList.as_view(), name='club-list'),
path('clubs/<int:pk>/', ClubDetail.as_view(), name='club-detail'),
path('coaches/', CoachList.as_view(), name='coach-list'),
path('coaches/<int:pk>/', CoachDetail.as_view(), name='coach-detail'),
]

With the above setup, you will be able to retrieve a list of coaches and their respective clubs, as well as update, delete and create coaches and clubs.

Here is an example response to /coaches/ endpoint:

[
{
"id": 1,
"name": "Jurgen Klopp",
"no_of_titles": 5,
"club": {
"id": 1,
"name": "Liverpool",
"nick_name": "The Reds",
"division": "Premier League",
"country": "England"
},
},
{
"id": 2,
"name": "Frank Lampard",
"no_of_titles": 0,
"club": null,
}
]
tip

In the serializers, many=False means that the relationship is one-to-one, and read_only=True means that the related field can only be retrieved, but not updated or created.

I hope this tutorial helps you get started with working with relationships and nested serializers in DRF!