Get Django Updates

Set up REST services with the Django REST framework

Problem

You want to start using the Django REST framework to build REST services, but don't know how to start.

Solution

Install the Django REST framework with pip install djangorestframework and add it a Django project's INSTALLED_APPS list. Create a Django REST framework serializer class to support your REST service. Create a REST service choosing between a regular Django view method, Django REST framework class based views, Django REST framework generic class views or Django REST framework views sets with routers.

How it works

Start by installing the Django REST framework with the command: pip install djangorestframework. Next, add the Django REST framework to the INSTALLED_APPS list variable in your Django project's settings.py file with the name rest_framework. That does it as far as installation is concerned, now let's walk through the core concepts and creation process of a REST service using the Django REST framework.

Serializers and views

Serializers are one of the main building blocks of the Django REST framework and are used to define the representation of objects. As I described in the previous recipe Introduction to REST services options for Django, Python objects can have ambiguous data representations (e.g. an object with a datetime field can be represented as DD/MM/YYYY, DD-MM-YYYY or MM-YYYY) and a serializer removes any uncertainty about how to represent an object. Listing 1 illustrates a Django REST framework serializer using its serializers package.

Listing 1 - Serializer class based on Django REST framework

from rest_framework import serializers

class StoreSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=200)
    email = serializers.EmailField()
    

As you can see listing 1, a Django REST framework serializer is strikingly similar to a Django model class or Django form class. In this case, you declare a standard Python class and inherit the behavior from the Django REST framework's serializers.Serializer class. Next, inside the serializer class you declare serializer fields using the various data types in the serializers package of the same Django REST framework.

The example in listing 1 is one of the simplest Django REST framework serializer classes possible, because it only has two fields and inherits its behavior from the very basic serializers.Serializer class. But just as with Django models and forms, as you progress with the Django REST framework, you'll find yourself using more advanced data types, as well as creating serializers with more sophisticated base classes than serializers.Serializer. I'll describe a more advanced serializer shortly, next let's explore a view in the context of the Django REST framework.

Serializer classes by themselves do nothing, they need to be integrated with views that do the bulk of the REST service logic (i.e. handle incoming requests, query a database for data) which then use serializers to transform data. Previously in Introduction to REST services options for Django, you also learned how a regular Django view method can be turned into a REST service. While it's perfectly possible to use a Django REST framework serializer class -- like the one listing 1 -- in a regular Django view method, the Django REST framework also provides additional view syntax -- illustrated in listing 2 -- to make it easier to build REST services.

Listing 2 - Django view method decorated with Django REST framework

from coffeehouse.stores.models import Store
from coffeehouse.stores.serializers import StoreSerializer

from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET','POST','DELETE'])
def rest_index(request):
    if request.method == 'GET':
        stores = Store.objects.all()
        serializer = StoreSerializer(stores, many=True)
        return Response(serializer.data)
    elif request.method == 'POST':
        ... #logic for HTTP POST  operation
    elif request.method == 'DELETE':
        ... #logic for HTTP DELETE operation

First notice the method in listing 2 is a regular Django view method, but it's decorated with @api_view from the Django REST framework. The arguments of @api_view indicate which HTTP REST methods to support -- see HTTP request methods for details on this topic, as this is a generic REST concept rather than a Django/REST topic. Next, inside the view method are a series of conditions to process the different HTTP request methods that form the REST service.

If a GET request is made on the view method, a query is made to get all Store objects. However, notice in this case we use the StoreSerializer from listing 1 to transform the Django queryset. In addition, the return statement uses the Django REST framework Response method, instead of Django's HttpResponse method.

More importantly, notice both the request and response logic in listing 2 lacks any kind of REST output format (i.e. JSON, XML). By leveraging the Django REST framework, you no longer need to deal with the low level details of detecting or handling output formats -- this is directly taken care of by the REST framework and based on how you make requests to the REST service, which I'll describe shortly.

Next, configure the rest_index() view method in listing 2 to become accessible at a URL, by adding a line like url(r'^rest/$',stores_views.rest_index,name="rest_index") to your app's urls.py file. Once you do this, access the URL and you'll see a result like the one in figure 1.

Django REST framework main service response
Figure 1.- Django REST framework main service response

As you can see in figure 1, the REST framework service response is very informative and pretty compared to Django's basic HttpResponse response generated by the Django REST service created in the past recipe. You can see the different options offered by the REST service and invoke its various operations in a few clicks. Also notice the output from the REST service only has Store objects with two fields due to the StoreSerializer definition in listing 1. Let's modify the serializer class to output complete Store objects. Listing 3 shows an updated serializer class based on listing 1.

Listing 3 - Serializer class using Django model based on Django REST framework

from rest_framework import serializers

from coffeehouse.stores.models import Store

class StoreSerializer(serializers.ModelSerializer):
    class Meta:
        model = Store
        fields = '__all__'
	    

Because we plan to serialize Store objects based on a Django model, listing 3 uses the Django REST framework ModelSerializer class to simplify the serializer syntax. Notice listing 3 only declares two arguments in the Meta class: model to specify the Django model you want to serialize and fields to indicate which fields to serialize. In this case, the __all__ value indicates all fields should be serialized, but you could equally assign a list of field names.

And it's as simple as that to serialize Django model classes for REST services using the Django REST framework. But as powerful as these Django REST framework serialization features are and as helpful as the Django REST framework view syntax used in listing 2 is -- cutting down on low level logic and presenting a user-friendly interface -- there's still a lot of scaffolding code in the view method that can be further trimmed down. To further simplify the construction of REST services, the Django REST framework makes use of a construct called class based views.

Class based views

Class based views are methods contained in a Python class to express all the operations of a REST service, as illustrated in listing 4.

Listing 4 - Django REST framework class based views

from coffeehouse.stores.models import Store
from coffeehouse.stores.serializers import StoreSerializer

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

class StoreList(APIView):

    def get(self, request, format=None):
        stores = Store.objects.all()
        serializer = StoreSerializer(stores, many=True)
        return Response(serializer.data)
	

    def post(self, request, format=None):
        ...
        #logic for HTTP POST operation


    def delete(self, request, format=None):
        ...
        #logic for HTTP DELETE operation


Notice in listing 4 how the class inherits its behavior from the Django REST framework APIView class. This allows the class to contain various methods representing each of the REST service's HTTP methods (i.e. GET,POST, DELETE). This produces much more understandable logic, compared to using a regular Django view method -- like the one in listing 2 -- which requires to manually inspect a request and perform conditional statements. The logic contained inside the get method in listing 4 uses the same Django REST framework syntax used in listing 2, so there's nothing new to explain there.

In the same way you need to hook up a regular Django view method to a URL, you must also associate Django REST framework class based views methods and make them accessible on a given URL. Listing 5 illustrates a urls.py file with the syntax to access the REST service class from listing 4.

Listing 5 - Django URL definition linked to Django REST framework class based views

from django.conf.urls import url
from coffeehouse.store import stores_views

urlpatterns = [
    url(r'^$',stores_views.index,name="index"),
    url(r'^rest/$',stores_views.StoreList.as_view(),name="rest_index"),
    ]

In listing 5 you can see the Django stores app urls.py file declares the URL pattern r'^rest/$' be mapped to the Django REST framework StoreList class which contains the class based views. In this manner, if a GET request is made on the /stores/rest/ URL it's handled by the def get() method of the class based view and if a POST request is made on the same /stores/rest/ it's handled by the def post() method of the class based view. It's worth mentioning that hitting the URL backed by the class based view methods from listing 4, also produce the same interface as figure 1 which is produced by the Django view method from listing 2.

Now, as helpful as REST framework class based views are to simplify REST service logic, class based views still require you to write all of the logic behind each method. For example, in the get() method in listing 4 you perform a query to get all store objects, then serialize the data and finally return a response. For the post() method in listing 4 you would similarly need to insert/update a store record with the provided data and with the delete() method you would need to delete a store record with the provided data.

Once you write a couple of class based views with the Django REST framework, you'll realize there's a constant pattern behind each type of view method (e.g. read a record, serialize it and return a response). On top of this, you'll also come to realize what a close relationship there is between REST methods (e.g.GET,POST and DELETE) and the operations they execute on Django models (i.e. Create-Read-Update-Delete(CRUD) operations).

To avoid having to constantly write the same CRUD operations and boilerplate logic for different Django objects associated with REST services, the Django REST framework offers another construct called mixin classes.

Mixins and generic class views

A mixin class is used to express the same logic and assign it to a class based view. For example, instead of writing the same logic from listing 4 -- get all objects, serialize them and generate a response -- over and over for different REST services (e.g. Item or Drink services) you could use the mixins.ListModelMixin class and quickly achieve the same result.

I won't go into greater detail about mixin classes, mainly because mixin classes are not as widely used as other Django REST framework options. Most Django framework REST services either use class based views -- to get full control over the logic -- or a more succinct approach based on mixins called mixed-in generic class views. Listing 6 illustrates an equivalent mixed-in generic class view based on the class based views from listing 4.

Listing 6 - Django mixed-in generic class views in Django REST framework

from coffeehouse.stores.models import Store
from coffeehouse.stores.serializers import StoreSerializer

from rest_framework import generics

class StoreList(generics.ListCreateAPIView):
    queryset = Store.objects.all()
    serializer_class = StoreSerializer
    

Notice listing 6 is even more succinct than prior iterations of the same REST service. In this case, the generic class name ListCreateAPIView is indicative of what this class produces -- A REST view to generate a list -- based on the queryset based on Store objects, using the serializer class from listing 3.

Just as before and even though you now have a REST service compromised of a couple of lines, the Django REST framework has even more functionality to offer. The two Django REST framework concepts I'm going to talk about next are called view sets and routers.

View sets and routers

The generic class view in listing 6 is pretty powerful for just three lines of code, but it's just one class to display a list of Store objects. Let's say you now wanted to create a REST service to a display a specific Store object, another REST service to update Store objects and yet another REST service to delete Store objects. In this scenario, you would need to create three more generic class views and three more URL mappings to roll out this functionality. But instead of creating separate view classes for each case, you could instead rely on a Django REST framework view set.

A Django REST framework view set, as its name implies is a group of views. To create a Django REST framework view set all you need to do is create a class that inherits its behavior from one of the Django REST framework's classes intended for this purpose. Listing 7 illustrates a view set created with the ModelViewSet class.

Listing 7 - Django viewset class in Django REST framework

from coffeehouse.stores.models import Store
from coffeehouse.stores.serializers import StoreSerializer

from rest_framework import viewsets

class StoreViewSet(viewsets.ModelViewSet):
    queryset = Store.objects.all()
    serializer_class = StoreSerializer

Listing 7 is as short as the REST service class in listing 6, but besides the class name change, it's the ModelViewSet class which gives this REST service a whole new dimension. Using this class alone, the REST service is automatically hooked up to display a Store object list, as well as create, get, update or destroy individual Store objects.

Because a view set generates multiple views, you're still left with the issue of configuring each view to a URL, in which case the easiest path is to use a Django REST framework router. A router is to a view set what a url statement is to class based view, a way to hookup an end point. Listing 8 illustrates the main urls.py file of a Django project set up with a Django REST framework router.

Listing 8 - Django URL definition with Django REST framework router for view set

from django.conf.urls import url
from coffeehouse.stores import views as stores_views

from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'stores', stores_views.StoreViewSet)


urlpatterns = [
    url(r'^rest/', include(router.urls,namespace="rest")),
    ]

The first step to hook up Django REST framework routers is to initiate a router with routers.DefaultRouter() and then register the different view sets with it. As you can see in listing 8, the router registration process uses the router.register method which accepts two arguments: the first argument to indicate the URL -- in this case stores -- and a second argument to specify the view set -- in this case the StoreViewSet class from listing 7.

Next in listing 8 you can see the router is assigned using Django's standard url and include methods. In this case, the router instance is assigned under the r'^rest/' URL of the main urls.py file, which means the final URLs for the Store view set would be under /rest/stores/.

And with this we conclude the introduction to setting up REST services with the Django REST framework. Now that you're familiar with the basics, in the next recipes you'll learn how to restrict, customize and perform other common tasks associated with REST services built the the Django REST framework.