class based views in Django Rest Framework


class based views in Django Rest Framework

Django Rest Framework supports both function and class based views. I personally prefer class based views upon function-based views. Because most of the common functionality was already implemented in the the class based views. so, why to write it again we can simply use it. It's always a better approach to keep our code DRY.

In DRF we have a class titled "APIView". It is the base class for all other generic API classes in django rest framework. It can be imported from "from rest_framework.views import APIView". Let's start with a simple API view.

Simple Hello world API View

# urls.py

from django.urls import path
from . import views

url_patterns = [
    path('api/hello-world/', views.HelloWorldAPIView.as_view(), name='hello_world'),
]

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response

class HelloWorldAPIView(APIView):
    def get(self, request, format=None):
        return Response(data={"message": "Hello World"}, status=200)

You can find source code of above hello world api app at github repo.

Now, how to test the API endpoint "/api/hello-world/"?. Before testing it let's install POSTMAN API APP (i.e used to test api andpoints). Please visit the url https://www.getpostman.com/downloads/ to download it. In above example we have seen a simple API returns {"message": "hello world"} (i.e JSON).

Generic views in Django REST Framework

Writing rest api's can be monotonous, because we repeat certain patterns again and again. No developer wants to copy-paste/re-write the same code again and they may get boredom at the view level. Django REST Framework’s generic views were developed to ease that pain.  They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to write too much code. So, it's better to use generic API views when requirement matches with existing functionality of generic views.

Django REST Framework provides the below generic views 

Class Based API Views

  1. CreateAPIView
  2. ListAPIView
  3. RetrieveAPIView
  4. DestroyAPIView
  5. UpdateAPIView

Extended Class Based API Views

  1. ListCreateAPIView
  2. RetrieveUpdateAPIView
  3. RetrieveDestroyAPIView
  4. RetrieveUpdateDestroyAPIView

Let's play with Generic or Class Based API Views

CreateAPIView

  • A generic api view provided by the DRF to  create data(i.e model instance) in a given model.
  • It is useful when there is a requirement to insert data into model through API endpoint.

Let's do some code now.

Let's write a simple api that can create a movie record in the database.

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('movie-create/', views.MovieCreateView.as_view(), name='movie_create'),
]

# models.py

from django.db import models

class Movie(models.Model):
    title = models.CharField(max_length=250)
    released_on = models.DateField()
    genre = models.CharField(max_length=250)
    director = models.CharField(max_length=250)

    def __str__(self):
        return self.title

# serializers.py

from rest_framework import serializers
from .models import *

class MovieSerializer(serializers.ModelSerializer):

    class Meta:
        model = Movie
        fields = '__all__'

# views.py

from rest_framework.generics import CreateAPIView
from .serializers import * 

class MovieCreateView(CreateAPIView):
    serializer_class = MovieSerializer

Now, open any api rest cient (I personally prefer "postman") and send a "POST" request to our api url "/api/movie-create/". Initially send an empty post request to it. It will ask for required fields.This is just for checking. Now, send a request with valid data like below

{
	"title": "Bahubali",
	"released_on": "2015-07-10",
	"genre": "fiction",
	"director": "SS Rajamouli"
}

And now, we can see the response like below with a http status code 201.

{
    "id": 1,
    "title": "Bahubali",
    "released_on": "2015-07-10",
    "genre": "fiction",
    "director": "SS Rajamouli"
}

ListAPIView

  • A generic api view provided by the DRF to list all data(i.e all model instances) in a given model.
  • It is useful when there is a requirement to retrieve all data from model through API endpoint.
  • To add pagination to the results then add the following configuration to "settings.py"
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
        'PAGE_SIZE': 100
    } 
    

Let's do some code now.

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('movie-list/', views.MovieListView.as_view(), name='movie_list'),
]

# views.py

from rest_framework.generics import ListAPIView
from .serializers import * 

class MovieListView(ListAPIView):
    queryset = Movie.objects.all()
    serializer_class = MovieSerializer

Now, open any api rest cient (I personally prefer "postman") and send a "GET" request to our api url "/api/movie-list/". Now, we get response like below with status code 200.

[
    {
        "id": 1,
        "title": "Bahubali",
        "released_on": "2015-07-10",
        "genre": "fiction",
        "director": "SS Rajamouli"
    }
]

This is a quick example to start with django rest "ListAPIView". It only allows us to send "GET" reuest if we send other requests like POST, PUT, DELETE then it will give us error message with status code 405.

RetrieveAPIView

  • A generic api view provided by the DRF to retrive a specific data from a given model (example: retrieve a unique user from users table).
  • It is useful when there is a requirement to retrieve a specific data instance through API endpoint.

In above generic list api view, we have retrieved all the available objects/rows in the model/tabe. What if we need a specific object from the model ?
Let's do some code now to know it

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('movie/<int:pk>/detail/', views.MovieRetrieveView.as_view(), name='movie_retrieve'),
]

# views.py

from rest_framework.generics import RetrieveAPIView
from .serializers import * 

class MovieRetrieveView(RetrieveAPIView):
    queryset = Movie.objects.all()
    serializer_class = MovieSerializer

Now, open any api rest cient (I personally prefer "postman") and send a "GET" request to our api url "/api/movie/1/detail/". Now, we get response like below with status code 200.

{
    "id": 1,
    "title": "Bahubali",
    "released_on": "2015-07-10",
    "genre": "fiction",
    "director": "SS Rajamouli"
}

It only allows us to send "GET" reuest if we send other requests like POST, PUT, DELETE then it will give us error message with status code 405.

DestroyAPIView

  • A generic api view provided by the DRF to delete a specific data (example: delete a specific user from users table) from a given model.
  • It is useful when there is a requirement to delete a specific data instance through API endpoint.

Let's do some code now.

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('movie/<int:pk>/delete/', views.MovieDestroyView.as_view(), name='movie_delete'),
]

# views.py

from rest_framework.generics import DestroyAPIView
from .serializers import * 

class MovieDestroyView(DestroyAPIView):
    queryset = Movie.objects.all()

"DestroyAPIView" only accepts request method "DELETE". To delete any specific object we have to provide "pk_urlkwarg" in url. Let's send a "DELETE" request to url "/api/movie/2/delete/" in this url "2" is a primary key of resource.
When you send a request it will return response of status code "204". If object is not found then we will recieve 404 status code.

UpdateAPIView

  • A generic api view provided by the DRF to update a specific data (example: update a specific user from users table) from a given model.
  • It is useful when there is a requirement to update a specific data instance through API endpoint.

Let's do some code now.

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('movie/<int:pk>/update/', views.MovieUpdateView.as_view(), name='movie_update'),
]

# views.py

from rest_framework.generics import RetrieveAPIView
from .serializers import * 

class MovieUpdateView(UpdateAPIView):
    queryset = Movie.objects.all()
    serializer_class = MovieSerializer

Now, open any api rest cient (I personally prefer "postman") and send a "PATCH" request to our api url "/api/movie/1/update/" with below data to update.

{
    "title": "Bahubali the beginning",
}

Now, we will get response like below.

{
    "id": 1,
    "title": "Bahubali The Beginning",
    "released_on": "2015-07-10",
    "genre": "fiction",
    "director": "SS Rajamouli"
}

"UpdateAPIView" only accepepts request type "PATCH" to update the resource if other request type is submitted then it will return status code of 405. Like "RetrieveAPIView" and "DestroyAPIView" it also requires "pk_urlkwarg" in url.

All above written code is tested and you can find the code in my github repository https://github.com/AnjaneyuluBatta505/learnbatta/tree/master/django-rest-framework/classbasedviews

Please let me know your thoughts in below comments.