Open In App

Class based views – Django Rest Framework

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Class-based views help in composing reusable bits of behavior. Django REST Framework provides several pre-built views that allow us to reuse common functionality and keep our code DRY. In this section, we will dig deep into the different class-based views in Django REST Framework.

This article assumes you are already familiar with Django and Django REST Framework. 

Checkout – 

APIView

APIView class provides commonly required behavior for standard list and detail views. With APIView class, we can rewrite the root view as a class-based view. They provide action methods such as get(), post(), put(), patch(), and delete() rather than defining the handler methods.

Creating views using APIView

Let’s take a look at how to create views using APIView. The views.py file module as follows:

Python3




from django.shortcuts import render
from django.http import Http404
  
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
  
from transformers.models import Transformer
from transformers.serializers import TransformerSerializer
  
class TransformerList(APIView):
    """
    List all Transformers, or create a new Transformer
    """
  
    def get(self, request, format=None):
        transformers = Transformer.objects.all()
        serializer = TransformerSerializer(transformers, many=True)
        return Response(serializer.data)
  
    def post(self, request, format=None):
        serializer = TransformerSerializer(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)
  
class TransformerDetail(APIView):
    """
    Retrieve, update or delete a transformer instance
    """
    def get_object(self, pk):
        # Returns an object instance that should 
        # be used for detail views.
        try:
            return Transformer.objects.get(pk=pk)
        except Transformer.DoesNotExist:
            raise Http404
  
    def get(self, request, pk, format=None):
        transformer = self.get_object(pk)
        serializer = TransformerSerializer(transformer)
        return Response(serializer.data)
  
    def put(self, request, pk, format=None):
        transformer = self.get_object(pk)
        serializer = TransformerSerializer(transformer, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  
    def patch(self, request, pk, format=None):
        transformer = self.get_object(pk)
        serializer = TransformerSerializer(transformer,
                                           data=request.data,
                                           partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
          
  
    def delete(self, request, pk, format=None):
        transformer = self.get_object(pk)
        transformer.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


The code is similar to regular Django views, but there is a better separation between different HTTP Methods.

  • The get() method process the HTTP GET request
  • The  post() method process the HTTP POST request
  • The put() method process the HTTP PUT request
  • The patch() method process the HTTP PATCH request
  • The delete() method process the HTTP DELETE request

Setting URL Configuration

Since we are using class-based views the way we mention the views in the path of the urls.py file is slightly different. Create a new file named urls.py (if not exist) in the app (transformers) folder and add the below code

Python3




from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from transformers import views
  
urlpatterns = [
    path('transformers/', views.TransformerList.as_view()),
    path('transformers/<int:pk>/', views.TransformerDetail.as_view()),
]
  
urlpatterns = format_suffix_patterns(urlpatterns)


Next, set up the root URL configuration. You can open the urls.py (same folder where the settings.py file is  located) and add the below code

Python3




from django.contrib import admin
from django.urls import path, include
  
urlpatterns = [
    path('', include('transformers.urls')),
]


Composing and sending HTTP Requests

1. Create a new entry – 

The HTTPie command is:

http POST :8000/transformers/ name=”Optimus Prime” alternate_mode=”1979 Freightliner Semi” description=”Optimus Prime is the strongest and most courageous of all Autobots, he is also their leader” alive=”True”

Output

HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 194
Content-Type: application/json
Date: Sat, 23 Jan 2021 03:48:46 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 Freightliner Semi",
    "description": "Optimus Prime is the strongest and most courageous of all Autobots, he is also their leader",
    "id": 1,
    "name": "Optimus Prime"
}

Sharing the command prompt screenshot for your reference:

2. Retrieve an existing entry

The pk value of Optimus Prime is 1. Let’s pass the pk value and retrieve the details

The HTTPie command is:

http GET :8000/transformers/1/

Output

HTTP/1.1 200 OK
Allow: GET, HEAD, OPTIONS
Content-Length: 194
Content-Type: application/json
Date: Sat, 23 Jan 2021 03:50:42 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 Freightliner Semi",
    "description": "Optimus Prime is the strongest and most courageous of all Autobots, he is also their leader",
    "id": 1,
    "name": "Optimus Prime"
}

Sharing the command prompt screenshot for your reference

3. Update an existing entry

Let’s update the field named alive by setting it to False. The HTTPie command is:

 http PUT :8000/transformers/1/ name=”Optimus Prime” alternate_mode=”1979 Freightliner Semi” description=”Optimus Prime is the strongest and most courageous of all Autobots, he is also their leader” alive=”False”

Output

HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 195
Content-Type: application/json
Date: Sat, 23 Jan 2021 04:22:30 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": false,
    "alternate_mode": "1979 Freightliner Semi",
    "description": "Optimus Prime is the strongest and most courageous of all Autobots, he is also their leader",
    "id": 1,
    "name": "Optimus Prime"
}

Sharing the command prompt screenshot for your reference

4. Update partially an existing entry

Let’s partially update the field named description. The HTTPie command is:

http PATCH :8000/transformers/1/ description=”Optimus Prime is the strongest and most courageous and leader of all Autobots”

Output

HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 181
Content-Type: application/json
Date: Sat, 23 Jan 2021 04:32:40 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": false,
    "alternate_mode": "1979 Freightliner Semi",
    "description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
    "id": 1,
    "name": "Optimus Prime"
}

Sharing the command prompt screenshot

5. Delete an existing entry

We will create a new entry and delete it. Let’s create a ‘Test’ entry using the below HTTPie command:

http POST :8000/transformers/ name=”Test”

Output

HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 77
Content-Type: application/json
Date: Sat, 23 Jan 2021 04:34:41 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": false,
    "alternate_mode": null,
    "description": null,
    "id": 2,
    "name": "Test"
}

Now let’s delete the ‘Test’ entry (pk =2). The HTTPie command to delete entry is

http DELETE :8000/transformers/2/

Output

HTTP/1.1 204 No Content
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 0
Date: Sat, 23 Jan 2021 04:35:06 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

Sharing the command prompt screenshot

Mixins

Mixin classes allow us to compose reusable bits of behavior. They can be imported from rest_framework.mixins. Let’s discuss the different types of mixin classes

  • ListModelMixin : It provides a .list(request, *args, **kwargs) method for listing a queryset.  If the queryset is populated, the response body has a 200 OK response with a serialized representation of the queryset.
  • CreateModelMixin: It provides a .create(request, *args, **kwargs) method for creating and saving a new model instance. If the object is created, the response body has a 201 Created response, with a serialized representation of the object. If invalid, it returns a  400 Bad Request response with the error details.
  • RetrieveModelMixin: It provides a .retrieve(request, *args, **kwargs) method for returning an existing model instance in a response. If an object can be retrieved, the response body has a 200 OK response, with a serialized representation of the object. Otherwise, it will return a 404 Not Found.
  • UpdateModelMixin: It provides a .update(request, *args, **kwargs) method for updating and saving an existing model instance. It also provides a .partial_update(request, *args, **kwargs) method for partially updating and an existing model instance. . If the object is updated, the response body has a 200 OK response, with a serialized representation of the object. Otherwise, 400 Bad Request response will be returned with the error details.
  • DestroyModelMixin: It provides a .destroy(request, *args, **kwargs) method for deleting an existing model instance. If an object is deleted, the response body has a 204 No Content response, otherwise, it will return a 404 Not Found.

Note: We will be using GenericAPIView to build our views and adding in Mixins. 

Creating Views using Mixins

Let’s take a look at how we can make use of Mixin classes. The views.py file module as follows:

Python3




from rest_framework import mixins
from rest_framework import generics
from transformers.models import Transformer
from transformers.serializers import TransformerSerializer
  
class TransformerList(mixins.ListModelMixin,
                      mixins.CreateModelMixin,
                      generics.GenericAPIView):
    queryset = Transformer.objects.all()
    serializer_class = TransformerSerializer
  
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
  
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
  
class TransformerDetail(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
    queryset = Transformer.objects.all()
    serializer_class = TransformerSerializer
  
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
  
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
  
    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
  
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


Here, the GenericAPIView class provides the core functionality, and we are adding mixin classes to it. The queryset and serializer_class are the basic attributes of GenericAPIView class. The queryset attribute is used for returning objects from this view and the serializer_class attribute is used for validating, deserializing input, and for serializing output.

In the TransformerList class, we make use of mixin classes that provide .list() and .create() actions and bind the actions to the get() and post() methods. In the TransformerDetail class we make use of mixin classes that provide .retrieve(), .update(), .partial_update(), and . destroy() actions and bind the actions to get(), put(), patch(), and delete() methods.

Composing and Sending HTTP Requests

1. Create a new entry

The HTTPie command is

http POST :8000/transformers/ name=”Bumblebee” alternate_mode=”1979 VW Beetle” description=”Small, eager, and brave, Bumblebee acts as a messenger, scout, and spy” alive=”True”

Output

HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 161
Content-Type: application/json
Date: Sat, 23 Jan 2021 04:58:26 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 VW Beetle",
    "description": "Small, eager, and brave, Bumblebee acts as a messenger, scout, and spy",
    "id": 3,
    "name": "Bumblebee"
}

2. Retrieve all entries

The HTTPie command is

http GET :8000/transformers/

Output

HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 345
Content-Type: application/json
Date: Sat, 23 Jan 2021 04:59:42 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "alive": true,
        "alternate_mode": "1979 VW Beetle",
        "description": "Small, eager, and brave, Bumblebee acts as a messenger, scout, and spy",
        "id": 3,
        "name": "Bumblebee"
    },
    {
        "alive": false,
        "alternate_mode": "1979 Freightliner Semi",
        "description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
        "id": 1,
        "name": "Optimus Prime"
    }
]

Sharing the command prompt screenshot for your reference

Generic class-based views

To make use of generic class-based views, the view classes should import from rest_framework.generics.

  • CreateAPIView: It provides a  post method handler and it is used for create-only endpoints. CreateAPIView extends GenericAPIView and CreateModelMixin
  • ListAPIView: It provides a get method handler and is used for read-only endpoints to represent a collection of model instances. ListAPIView extends GenericAPIView and ListModelMixin.
  • RetrieveAPIView: It provides a get method handler and is used for read-only endpoints to represent a single model instance. RetrieveAPIView extends GenericAPIView and RetrieveModelMixin.
  • DestroyAPIView: It provides a delete method handler and is used for delete-only endpoints for a single model instance. DestroyAPIView extends GenericAPIView and DestroyModelMixin.
  • UpdateAPIView: It provides put and patch method handlers and is used for update-only endpoints for a single model instance. UpdateAPIView extends GenericAPIView and UpdateModelMixin.
  • ListCreateAPIView: It provides get and post method handlers and is used for read-write endpoints to represent a collection of model instances. ListCreateAPIView extends GenericAPIView, ListModelMixin, and CreateModelMixin..
  • RetrieveUpdateAPIView: It provides get, put, and patch method handlers. It is used to read or update endpoints to represent a single model instance. RetrieveUpdateAPIView extends GenericAPIView, RetrieveModelMixin, and UpdateModelMixin.
  • RetrieveDestroyAPIView: It provides get and delete method handlers and it is used for read or delete endpoints to represent a single model instance. RetrieveDestroyAPIView extends GenericAPIView, RetrieveModelMixin, and DestroyModelMixin.
  • RetrieveUpdateDestroyAPIView: It provides get, put, patch, and delete method handlers. It is used for read-write-delete endpoints to represent a single model instance. It extends GenericAPIView, RetrieveModelMixin, UpdateModelMixin, and DestroyModelMixin.

Creating views using generic class-based views

Let’s take a look at how we can make use of Mixin classes. Here we will take advantage of  ListCreateAPIView and RetrieveUpdateDestroyAPIView. The views.py file module as follows:

Python3




from rest_framework import generics
  
from transformers.models import Transformer
from transformers.serializers import TransformerSerializer
  
class TransformerList(generics.ListCreateAPIView):
    queryset = Transformer.objects.all()
    serializer_class = TransformerSerializer
  
class TransformerDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Transformer.objects.all()
    serializer_class = TransformerSerializer


You can notice that we were able to avoid a huge amount of boilerplate code. These generic views combine reusable bits of behavior from mixin classes. Let’s look at the declaration of ListCreateAPIView and RetrieveUpdateDestroyAPIView:

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
   ......

class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                                   GenericAPIView):
  ......

Composing and sending HTTP Requests

1. Create a new entry

The HTTPie command is

http POST :8000/transformers/ name=”Cliffjumper” alternate_mode=”1979 Porsche 924″ description=”His eagerness and daring have no equal” alive=”True”

Output

HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 133
Content-Type: application/json
Date: Sat, 23 Jan 2021 05:28:45 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 Porsche 924",
    "description": "His eagerness and daring have no equal",
    "id": 5,
    "name": "Cliffjumper"
}

Sharing the command prompt screenshot for your reference

2. Update an existing entry

The HTTPie command is

http PUT :8000/transformers/5/ name=”Cliffjumper” alternate_mode=”1979 Porsche 924″ description=”Eager and Daring” alive=”True”

Output

HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 111
Content-Type: application/json
Date: Sat, 23 Jan 2021 05:35:39 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 Porsche 924",
    "description": "Eager and Daring",
    "id": 5,
    "name": "Cliffjumper"
}

Sharing the command prompt screenshot for your reference

3. Partially update an existing entry

http PATCH :8000/transformers/3/ alive=”True”

Output

HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 151
Content-Type: application/json
Date: Sat, 23 Jan 2021 05:37:54 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 VW Beetle",
    "description": "Small, eager, and brave. Acts as a messenger, scout, and spy",
    "id": 3,
    "name": "Bumblebee"
}

Sharing the command prompt screenshot for your reference

In this section, we explored different types of class-based views provided by the Django REST Framework. We implemented views using APIView and explained different types of mixin classes. Finally, we revealed various types of generic class-based views and demonstrated how they avoid a huge amount of boilerplate code.



Last Updated : 16 Feb, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads