171
votes

I am beginner in Django REST framework and need your advice. I am developing a web service. The service has to provide REST interface to other services. The REST interface, which I need to implement, is not working with my models directly (I mean the get, put, post, delete operations). Instead, it provides other services with some calculation results. On a request my service makes some calculations and just returns the results back (doesn't store the results in its own database).

Below is my understanding of how that REST interface could be implemented. Correct me, if I am wrong.

  1. Create class which makes the calculations. Name it 'CalcClass'. CalcClass uses the models in its work.
    • Params necessary for the calculations are passed to the constructor.
    • Implement the calc operation. It returns results as 'ResultClass'.
  2. Create ResultClass.
    • Derived from object.
    • It just only has attributes containing the calc results.
    • One part of the calc results is represented as tuple of tuples. As I understand, it would be better for further serialization to implement a separate class for those results and add list of such objects to ResultClass.
  3. Create Serializer for ResultClass.
    • Derive from serializers.Serializer.
    • The calc results are read-only, so use mostly Field class for fields, instead of specialized classes, such as IntegerField.
    • I should not impl save() method neither on ResultClass, nor on Serializer, because I am not going to store the results (I just want to return them on request).
    • Impl serializer for nested results (remember tuple of tuples mentioned above).
  4. Create View to return calculation results.
    • Derive from APIView.
    • Need just get().
    • In get() create CalcClass with params retrieved from the request, call its calc(), get ResultClass, create Serializer and pass the ResultClass to it, return Response(serializer.data).
  5. URLs
    • There is no api root in my case. I should just have URLs to get various calc results (calc with diff params).
    • Add calling format_suffix_patterns for api browsing.

Did I miss something? Is the approach is correct in general?

1
This approach is correct and to me actually looks more elegant than the accepted answer (results data encapsulated in a reusable result type). But at the end of the day this is mostly a question of personal preference and both approaches do the job.zepp133

1 Answers

170
votes

Django-rest-framework works well even without tying it to a model. Your approach sounds ok, but I believe you can trim some of the steps to get everything working.

For example, rest framework comes with a few built-in renderers. Out of the box it can return JSON and XML to the API consumer. You can also enable YAML by just installing the required python module. Django-rest-framework will output any basic object like dict, list and tuple without any extra work on your part.

So basically you only have to create the function or class that takes in arguments, does all of the required calculations and returns its results in a tuple to the REST api view. If JSON and/or XML fits your needs, django-rest-framework will take care of the serialization for you.

You can skip steps 2 and 3 in this case, and just use one class for calculations and one for presentation to the API consumer.

Here are a few snippets may help you out:

Please note that I have not tested this. It's only meant as an example, but it should work :)

The CalcClass:

class CalcClass(object):

    def __init__(self, *args, **kw):
        # Initialize any variables you need from the input you get
        pass

    def do_work(self):
        # Do some calculations here
        # returns a tuple ((1,2,3, ), (4,5,6,))
        result = ((1,2,3, ), (4,5,6,)) # final result
        return result

The REST view:

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

from MyProject.MyApp import CalcClass


class MyRESTView(APIView):

    def get(self, request, *args, **kw):
        # Process any get params that you may need
        # If you don't need to process get params,
        # you can skip this part
        get_arg1 = request.GET.get('arg1', None)
        get_arg2 = request.GET.get('arg2', None)

        # Any URL parameters get passed in **kw
        myClass = CalcClass(get_arg1, get_arg2, *args, **kw)
        result = myClass.do_work()
        response = Response(result, status=status.HTTP_200_OK)
        return response

Your urls.py:

from MyProject.MyApp.views import MyRESTView
from django.conf.urls.defaults import *

urlpatterns = patterns('',
    # this URL passes resource_id in **kw to MyRESTView
    url(r'^api/v1.0/resource/(?P<resource_id>\d+)[/]?$', login_required(MyRESTView.as_view()), name='my_rest_view'),
    url(r'^api/v1.0/resource[/]?$', login_required(MyRESTView.as_view()), name='my_rest_view'),
)

This code should output a list of lists when you access http://example.com/api/v1.0/resource/?format=json. If using a suffix, you can substitute ?format=json with .json. You may also specify the encoding you wish to get back by adding "Content-type" or "Accept" to the headers.

[
  [
    1, 
    2, 
    3
  ], 
  [
    4, 
    5, 
    6
  ]
]

Hope this helps you out.