Implementing Views and Renderers in Django REST Framework
Django REST Framework (DRF) provides a powerful set of tools for building APIs. Among the most important components are Views, which handle request logic, and Renderers, which manage the output format. This guide explores the progression from low-level APIView to the high-level ModelViewSet, as well as how to control data presentation using Renderers.
Data Preparation
Before implementing views, we need a serializer to convert model instances into JSON data. Below is a standard ModelSerializer for a Role model.
from rest_framework import serializers
from .models import Role
class RoleModelSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = "__all__"
Working with APIView
The APIView class is the most basic view provided by DRF. When using it, you are responsible for handling pagination and serialization logic manually within the request methods.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from .models import Role
from .serializers import RoleModelSerializer
class RolePagination(PageNumberPagination):
page_size = 5
page_size_query_param = 'size'
max_page_size = 20
class RoleListAPIView(APIView):
def get(self, request, *args, **kwargs):
# Retrieve data from database
queryset = Role.objects.all()
# Initialize pagination
paginator = RolePagination()
paginated_queryset = paginator.paginate_queryset(queryset, request, view=self)
# Serialize the paginated result
serializer = RoleModelSerializer(instance=paginated_queryset, many=True)
# Return paginated response structure
return paginator.get_paginated_response(serializer.data)
To access this view, define a route in urls.py:
from django.urls import path
from .views import RoleListAPIView
urlpatterns = [
path('roles/', RoleListAPIView.as_view()),
]
Streamlining Logic with GenericAPIView
GenericAPIView extends APIView by providing commonly used attributes like queryset and serializer_class. This reduces boilerplate code by offering helper methods like get_queryset() and get_serializer().
from rest_framework.generics import GenericAPIView
class RoleGenericListView(GenericAPIView):
queryset = Role.objects.all()
serializer_class = RoleModelSerializer
pagination_class = RolePagination
def get(self, request, *args, **kwargs):
roles = self.get_queryset()
page = self.paginate_queryset(roles)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(roles, many=True)
return Response(serializer.data)
Utilizing GenericViewSet
The GenericViewSet combines GenericAPIView logic with the flexibility of ViewSets. Instead of defining get or post methods, you define actions like list or create. The mapping between HTTP methods and actions is handled in the URL configuration.
from rest_framework.viewsets import GenericViewSet
class RoleViewSet(GenericViewSet):
queryset = Role.objects.all()
serializer_class = RoleModelSerializer
pagination_class = RolePagination
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
In urls.py, you must specify the method mapping:
urlpatterns = [
path('roles/', RoleViewSet.as_view({'get': 'list'})),
]
Efficiency with ModelViewSet
ModelViewSet is the most automated way to create a CRUD interface. It inherits from multiple mixins (List, Create, Retrieve, Update, Destroy), meening you often don't need to write any method logic at all.
from rest_framework.viewsets import ModelViewSet
class RoleFullViewSet(ModelViewSet):
queryset = Role.objects.all()
serializer_class = RoleModelSerializer
pagination_class = RolePagination
The routing for ModelViewSet can handle both collection and instance endpoints:
urlpatterns = [
path('roles/', RoleFullViewSet.as_view({'get': 'list', 'post': 'create'})),
path('roles/<int:pk>/', RoleFullViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})),
]
Controlling Output with Renderers
Renderers determine how the data is displayed to the client (e.g., as JSON or via a web-based UI). You can configure renderers globally or at the view level.
Global Configuration
In settings.py, you can define the default renderers for the entire project:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}
Local Configuration
To restrict a specific view to only one format, such as JSON, use the renderer_classes attribute:
from rest_framework.renderers import JSONRenderer
class SecureRoleView(APIView):
renderer_classes = [JSONRenderer]
def get(self, request):
data = {"message": "This is only available as JSON"}
return Response(data)