RESTful Representations for Django Views

Posted on May 29, 2007

This is more of a hack-ish concept (or what is sometimes referred to as the “lazy-hobo way”) that will need some refactoring before it goes into full use, but I wanted to present the idea so that other people could comment on it and offer some suggestions (or criticisms, be that as it may). Hopefully the Django community will find this bit useful enough to convince me to post it on djangosnippets.

Basically I’ve been working on a project where a number of our views required multiple representations (ie: the same dataset rendered in html or xml). For deadline reasons we simply added a “_xml” suffix to a copy of every view that needed to render an XML template. Of course, this is a violation of the DRY principles, and so I ended up looking for a way to refactor the code.

For me, the REST approach seemed most natural. I could then use the URI to tell the view how it should render itself. Syntactically I thought that using Python decorators would be the best approach. Here’s (sort of) what I was thinking it would look like:

@response_types('xml', 'json')
def my_view(request, response_type):
    # ...
    if response_type == 'xml':
        # return xml template
    elif response_type == 'json':
        # return json-serialized string
    else:
        # default to html

There are a few things I want to refactor off the bat. First, I’d like to remove ‘response_type’ name from the function declaration; it would be cleaner to wrap all the functionality from the decorator.

This solution also won’t work with generic views.

Anyway, the decorator ended up looking like this:

import re

def response_types(*types):
    def decorator(func):
        def inner(request, *args, **kwargs):
            if request.GET.has_key('view'):
                requested_type = request.GET['view']
            else:
                requested_type = 'html'
            if requested_type not in types:
                kwargs['response_type'] = 'html'
            else:
                kwargs['response_type'] = requested_type
            return func(request, *args, **kwargs)
        return inner
    return decorator

And so far as a concept, it works. From a single urlconf to my view, I can tell the view to load an xml template or json string or default to html with a single decorator on my view method. With a little touching up, I hope this approach can reduce the amount of code in my views.

/my_app/index -- will default to html
/my_app/index/?view=xml -- will render xml template from the decorated view

As for discussion… I may turn on comments soon when I ‘officially’ launch this site, but for now I accept email. You can send one to my first name at this domain. Cheers.

Update:
Cleaned up the decorator code to fix a slight bug that was failing to pass the default value ‘html’ to the response_type keyword argument.