Working with views using DRF
DRF extends the views concept of Django to provide a better interface for creating REST endpoints. Just like functional and class-based views in Django, DRF also supports both of them. However, it depends on the developer to choose which type of view fits their use case.
As a rule of thumb, I am always inclined toward functional views when I have a standalone endpoint where the logic is straightforward and won’t have any complexity in the future. Class-based views have a learning curve that stops developers from using them initially, but once the entry barrier is breached, developers rarely move back to functional views. Let’s explore both types of views in more detail.
Functional views
Django provides an easy functional view interface that helps any developer get going with faster development; that is why it is very popular for any developer starting Django. DRF keeps the simplicity of Django views and allows developers to convert a Django view into DRF using a simple decorator, @api_view
. I prefer using it when the logic is straightforward or when I’m working on a simple project that will not get too complex.
DRF gives us @api_view
to work with regular Django functional views. Adding the decorator converts the usual Django HttpRequest
into a Request
instance. The decorator takes a list of all the HTTP-allowed methods for the given functional view block; any method that is not listed will not be allowed. By default, if no HTTP method is mentioned, then it will only allow GET
methods. For a response to views, we should use the Response
class from DRF rather than HttpResponse
from Django; it automatically takes care of bypassing cross-site request forgery (CSRF) for views and gives us a UI to interact with the backend service.
Let’s look at an example of a DRF functional view:
from rest_framework.decorators import api_view from rest_framework.response import Response @api_view(['GET', 'POST', 'PUT']) def hello_world(request, *args, **kwargs): if request.method == 'POST': return Response(data={'msg': 'POST response block'}) elif request.method == 'PUT': return Response(data={'msg': 'PUT response block'}) return Response(data={'msg': 'GET response block'})
Now, let’s learn how to work with class-based views, which are widely used throughout the industry.
Class-based views
As mentioned previously, class-based views have a learning curve, so they’re avoided initially by a lot of developers. Even I avoided it for a couple of months until I saw the bigger picture, but ever since, there has been no looking back. It takes advantage of the inheritance property and helps implement the Don’t Repeat Yourself (DRY) principle. For large code bases, it is an absolute must, and I recommend that anyone starting a new project implement it from the start.
DRF provides two types of class-based views: APIView
and Generic Views.
APIView
The DRF APIView class is an extension of Django’s View
class. Using the APIView
class converts the default Django HttpRequest
into a Request
object, and the handler methods can return DRF’s Response
object rather than Django’s HttpResponse
. It supports additional policy attributes such as authentication_classes
, permission_classes
, and versioning_classes
, which make the life of a developer much easier. We shall use them in the following chapters and discuss them in detail.
The interface for using APIView
is simple: to implement any HTTP request method for a given endpoint, simply create a new method in the class with the given name; for example, a GET
request would have a get method, and a DELETE
request would have a delete method, and so on. Whenever a request is hit for the given HTTP method, the corresponding method will be called automatically.
Let’s implement a quick example with APIView
to learn more:
from rest_framework.views import APIView class DemoAPIView(APIView): def get(self, request, *args, **kwargs): return Response(data={'msg': 'get request block'}) def post(self, request, *args, **kwargs): return Response(data={'msg': 'post request block'}) def delete(self, request, *args, **kwargs): return Response(data={'msg': 'delete request block'})
Linking an APIView
implemented class to the urls.py
file is different from how we linked functional views earlier. Instead, we use <class name>.as_view()
to link it to the corresponding URL:
urlpatterns = [ ... path('apiview-class/', views.DemoAPIView.as_view()) ]
We shall expand our knowledge of APIView
more as we learn about different concepts surrounding DRF and Django.
Generic Views
While building a web application, there comes a point when developers are doing the same monotonous job of writing repetitive logic. That is when the principle of DRY kicks in and the developer thinks about how to solve this repetitive pattern. The answer to this is Generic Views.
Generic Views is one of the most widely popular features of Django and DRF that helps developers build a basic CRUD operation API at lightning speed. Generic Views are tightly coupled with DRF’s serializer and model concepts, so we shall discuss Generic Views in detail once we learn about these concepts in the following chapters.
Opinionated note
We are not discussing Viewsets here since it is primarily used with Router. We have already discussed why we should avoid Router in Django, but if you are keen to learn more about it, you can go to the DRF documentation, which explains it quite well with relevant examples.
For more details about Viewsets
, see https://www.django-rest-framework.org/api-guide/viewsets/.
Now that we have seen the basic integration of DRF and how to work with views, let’s focus on how to improve the development experience. Web development is more than just writing code – it is also about using the right tools. Throughout this book, we shall introduce more and more tools for different purposes. For now, let’s learn about API development tools.