Get Django Updates

Introduction to REST services options for Django

Problem

You want to create REST services with Django and understand the available options.

Solution

To create a REST service in Django with a standard view method ensure the data you want to output is serializable to the desired format (e.g. XML or JSON). To serialize data you can use Python's core json package, the Python dict2xml package, as well as Django's own django.core.serializers package which supports serialization to XML and JSON.

To create a REST service response in a standard view method you can use HttpResponse to output the data directly or the short-cut render method to send the output to a template for further formatting. Irrespective of the response technique you use, it's important you specify the content_type argument with a MIME type (e.g. application/json, application/xml) to ensure the calling party knows the type of output. Django also offers JsonResponse -- which is a shortcut version of HttpResponse -- that automatically converts data to JSON and uses the application/json MIME type.

For more elaborate Django REST services projects, you can use the Django REST framework or Django Tastypie framework, both of which have a series of features and constructs designed to shorten the development time of REST services in Django.

How it works

Representational state transfer (REST) services or simply RESTful services have become one of the most popular techniques in web development since they first appeared in the year 2000. While there's a long background story on REST services which I won't get into here, the appeal and explosive growth of REST services in web development is simply due to how they solve a common problem in an easy and reusable manner.

A REST service provides access to data through an end point or Internet URL making no assumption about who uses it (e.g. a mobile phone, a desktop browser, another program), this means there's no device or environment requirements to access REST services, all you need is Internet access. On top of this, because REST services operate on Internet URLs, they provide a very intuitive access scheme. For example, the REST service URL /products/ can mean 'get all product data', while the URL /products/10/20/ can mean get all data for products with a price between 10 and 20 and the URL /products/drinks/ get all product data in the drinks category. The power of this last approach is there's no steep learning curve (e.g. language syntax, complex business logic) to access REST service variations that solve elaborate data queries.

Because of these features and versatility, REST services work as a reusable data backbone across a wide array of areas and applications. For example, practically the entire web API (Application Programming Interface) world operates around REST web services, because by definition an API must provide a device/environment neutral way for customers to access their functionality. In fact, there's a high-chance most web sites you visit make us of REST services in one way or another, a practice that allows web site operators to reuse the same data and then format it for users on desktop browsers, mobile applications, RSS feeds or any other target like Accelerated Mobile Pages(AMP).

With this brief overview of REST services, let's jump to the first and simplest option available to create REST services in Django.

Standard view method designed as REST service

You can actually create a REST service in Django with just the Django package and Python's core packages, with no need to install any third party package. Listing 1 illustrates a standard Django view method designed to function as a REST service.

Listing 1 - Standard view method designed as REST service

from django.http import HttpResponse
from coffeehouse.stores.models import Store
import json 


def rest_index(request):
    store_list = Store.objects.all()
    store_names = [{"name":store.name} for store in store_list]
    return HttpResponse(json.dumps(store_names), content_type='application/json')


# Sample output
# [{"name": "Corporate"}, {"name": "Downtown"}, {"name": "Uptown"}, {"name": "Midtown"}]

The view method in listing 1 first makes a query to get all Store instances from a database, then creates a list comprehension to generate a list of store names from the resulting query and finally uses Django's HttpResponse to return a JSON response with the store names using Python's json package. The design in listing 1 is one of the simplest REST services you can create in Django and has some important points:

Note Error: is not JSON/XML serializable

One of the most common errors you'll likely encounter when you create REST services in Django is the is not JSON/XML serializable error. This just means Django isn't capable of serializing (i.e. representing) the source data into the selected format -- JSON or XML. This error happens when the source data is made up of objects or data types that can produce undetermined or ambiguous serializing results. For example, if you have a Store model class with relationships, how should Django serialize these relationships ? Similarly, if you have a Python datetime instance how should Django serialize the value, as DD/MM/YYYY, DD-MM-YYYY or something else ? In every case Django never attempts to guess the serialization representation, so unless the source data is naturally serializable -- as in listing 1 -- you must explicitly specify a serialization scheme -- as I'll explain next -- or you'll get the is not JSON/XML serializable error.

Now that you have a brief understanding of a simple REST service in Django, let's rework the REST service in listing 1 to make use of parameters so it can return different data results and data formats.

Listing 2 - Standard view method designed as REST service with parameters

from django.http import HttpResponse
from coffeehouse.stores.models import Store
from django.core import serializers


def rest_index(request,store_id=None):
    store_list = Store.objects.all()
    if store_id:
        store_list = store_list.filter(id=store_id)
    if 'type' in request.GET and request.GET['type'] == 'xml':
        serialized_stores = serializers.serialize('xml',store_list)
        return HttpResponse(serialized_stores, content_type='application/xml')
    else:
        serialized_stores = serializers.serialize('json',store_list)
        return HttpResponse(serialized_stores, content_type='application/json')


The first thing that's different in listing 2 is the view method can accept a store_id parameter, allowing the REST service to process a URL request for all stores (e.g./stores/rest/) or individual stores (e.g./stores/1/rest/ for store number 1). If you're unfamiliar on how to set up Django URLs with parameters, see the previous recipe Access url parameters, extra options and define default url parameters on Django view methods.

Once inside the view method we create a query to get all Store instances from a database and if there's a store_id value we further apply a filter on the query to limit the results to the given store_id. This last query logic is perfectly efficient due to the way Django queries work -- see Understanding a QuerySet: Lazy evaluation & caching for additional details on this topic.

Next, an inspection is made on the request to see if it has the type parameter with an xml value. If a request does match this last rule (e.g. /stores/rest/?type=xml) then we generate an XML response for the REST service, if it doesn't match this last rule then we generate a JSON response for the REST service.

While the example in listing 2 also uses HttpResponse and the content_type argument just like listing 1, notice the data is prepared with Django's django.core.serializers package which is designed to make data serialization easier -- unlike listing 1 which required pre-processing the query data and then using Python's json package to complete the process.

The use of django.core.serializers is very simple in listing 2, we use the serialize method which expects the serialization type as its first argument (e.g. 'xml' or 'json') and the data to serialize as the second argument (e.g. the store_list reference which represents the query). There's much more functionality behind the django.core.serializers package (e.g. filters) that I won't explore here to keep things simple, but figure 1 and 2 show two sample results from listing 2.

JSON output from Django REST service
Figure 1.- JSON output from Django REST service
XML output from Django REST service
Figure 2.- XML output from Django REST service

As you can see in figures 1 and 2, Django is capable of serializing a query and outputting its result into a valid JSON or XML REST service response with just the few lines in listing 2. It's likely these few lines from listing 2 can take you a long way toward building your own REST services in Django, but if you've never done REST services or plan to use REST services as the centerpiece of an application or web site, you should pause to analyze your requirements.

Because REST services solve a common problem in an easy and reusable manner they tend to suffer from scope creep, that characteristic where changes appear to be never ending and functionality is always 'not quite finished'. After seeing this initial Django REST service example, ask yourself the following questions:

If you said yes to most of the previous questions, then your REST services undertaking is beyond basic. While you could continue building on the example in listing 2 to support all of the previous scenarios -- using a standard Django view method and additional Python packages -- supporting these more elaborate REST services features in Django is a path many have gone down before, to the point there are dedicated packages for just this purpose.

Django REST framework (http://www.django-rest-framework.org/)

The Django REST framework is now in its 3rd version. Compared to writing your own REST service plumbing as I just described, the Django REST framework offers the following advantages:

These are just some of the core benefits of using the Django REST framework. As you can see, if you plan to create more than a couple of REST services, investing the time to learn the Django REST framework is well worth the time vs. dealing with the scaffolding code necessary to deploy REST services.

Django Tastypie framework (http://tastypieapi.org/)

The Django Tastypie framework emerged as an alternative to the Django REST framework. Although the Django Tastypie framework is in its 0.13.3 version, don't let the pre-1.0 release number fool you, the Django Tastypie framework has been in development since 2010. Although both the Django Tastypie framework and Django REST framework can potentially produce the same results, the Django Tastypie framework has some of these things going for it:

In the end all options are good. So even though the Django Tastypie framework is not as mainstream as the Django REST framework, if you feel overwhelmed creating REST services with the latter, you can always try out the former to arrive at a faster REST solution than building your REST services from scratch.