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[1] 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.

In this chapter, you'll learn about the options available to create REST services with Django, including: plain Python/Django REST services and framework specific REST services. In addition, you'll learn how to create REST services with plain Python/Django packages and when they're preferable over using framework specific REST services packages. In addition, you'll also learn how to create and use some of the most important features of REST services created with the Django REST framework, as well as incorporate essential security into Django REST framework services.

REST services in Django

A REST service provides access to data through an end point or Internet URL making no assumption about who uses it (e.g. an IoT device, a mobile phone or a desktop browser), 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, IoT devices, mobile applications, RSS feeds or any other target like Accelerated Mobile Pages(AMP)[2].

With this brief overview of REST services, let's start with the 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 12-1 illustrates a standard Django url and view method designed to function as a REST service.

Listing 12-1. Standard view method designed as REST service

# urls.py (In stores app)
from coffeehouse.stores import views as stores_views

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

# views.py (In stores app)
from django.http import HttpResponse
from coffeehouse.stores.models import Store
import json 

def rest_store(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 url statement in listing 12-1 defines an access pattern on the stores app under the rest directory (i.e. final url /stores/rest/ ). This last url statement is hooked up the rest_store view method -- also in listing 12-1 -- which makes a query to get all Store records from the 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 12-1 is one of the simplest REST services you can create in Django and has some important points:

Error: is not JSON/XML serializable

One of the most common errors you're likely to encounter when you create REST services in Django is the 'Is not JSON/XML serializable' error. This means Django isn't capable of serializing (i.e. representing/converting) 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 12-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 12-2 to make use of parameters so it can return different data results and data formats.

Listing 12-2. Standard view method as REST service with parameters and different output formats

# urls.py (In stores app)
from coffeehouse.stores import views as stores_views

urlpatterns = [
    url(r'^rest/$',stores_views.rest_store,name="rest_index"),
    url(r'^(?P<store_id>\d+)/rest/$',stores_views.rest_store,name="rest_detail"),
]

# views.py (In stores app)
from django.http import HttpResponse
from coffeehouse.stores.models import Store
from django.core import serializers

def rest_store(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 12-2 is an additional url statement that accepts requests with a store_id parameter (e.g. /stores/1/rest/, /stores/2/rest/). In this case, the new url is processed by the same rest_store() view method to process the stores index from listing 12-1. (i.e. /stores/rest/)

Next, the view method in listing 12-2 is a modified version of the view method in listing 12-1 which accepts a store_id parameter. This modification allows 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 Chapter 2 which describes url parameters.

Once inside the view method, a query is made to get all Store records from a database. In case the method receives a store_id value, an additional filter is applied 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 -- if you're unfamiliar with the behavior in Django queries, see Chapter 8 'Understanding a QuerySet' section.

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

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

The use of django.core.serializers is very simple in listing 12-2. The serialize() method is used 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 figures 12-1 and 12-2 show two sample results from the REST service in listing 12-2.

Figure 12-1. JSON output from Django REST serviceXML output from Django REST service

Figure 12-2. XML output from Django REST service

As you can see in figures 12-1 and 12-2, Django is capable of serializing a query and outputting its result as a REST service response in either JSON or XML, with just the few lines in listing 12-2. It's likely these few lines from listing 12-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 12-2 -- using a standard Django view method and additional Python packages -- to support all of the previous scenarios, supporting these more elaborate REST services features in Django is a path many have gone down before, to the point there are dedicated frameworks for just this purpose.

Django REST framework[3]

The Django REST framework is now in its 3rd version. Compared to writing your own REST services from plain Python/Django packages 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 realize, 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 using plain Python/Django packages.

Django Tastypie framework[4]

The Django Tastypie framework emerged as an alternative to the Django REST framework. Although the Django Tastypie framework is in its 0.14 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 these differences:

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 with plain Python/Django packages.

  1. https://en.wikipedia.org/wiki/Representational_state_transfer    

  2. https://www.ampproject.org/    

  3. http://www.django-rest-framework.org/    

  4. http://tastypieapi.org/    

  5. https://djangopackages.org/grids/g/rest/    

  6. https://djangopackages.org/grids/g/search/