Login

Simple REST APIs

Author:
jacobian
Posted:
September 23, 2009
Language:
Python
Version:
1.1
Score:
8 (after 8 ratings)

This is how I tend to roll simple REST APIs. It's by no means as full-featured as something like Piston, but it's simple, clean, and it works.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
from django import http
from django.conf import settings
from django.core import urlresolvers
from django.shortcuts import get_object_or_404, redirect
from django.utils import simplejson
from django.utils.encoding import force_unicode
from yabl.authors.models import Author

class RESTResource(object):
    """
    Dispatches based on HTTP method.
    """
    # Possible methods; subclasses could override this.
    methods = ['GET', 'POST', 'PUT', 'DELETE']
    
    def __call__(self, request, *args, **kwargs):
        callback = getattr(self, request.method, None)
        if callback:
            return callback(request, *args, **kwargs)
        else:
            allowed_methods = [m for m in self.methods if hasattr(self, m)]
            return http.HttpResponseNotAllowed(allowed_methods)

class AuthorResource(RESTResource):
    """
    Base class for all Author resources providing conversion between Author
    objects and simple flat dicts.
    """
    def format(self, author, full=False):
        """
        Convert from an Author object to a dict.
        """
        link = urlresolvers.reverse('api_author_detail', args=[author.pk])
        info = {
            'first_name': author.first_name,
            'last_name': author.last_name,
            'link': link,
        }
        if full:
            info['bio'] = author.bio
        return info
        
    def parse(self, d):
        """
        Convert from a dict to an Author object.
        """
        try:
            return Author(
                first_name = force_unicode(d['first_name']),
                last_name = force_unicode(d['last_name']),
                bio = force_unicode(d.get('bio', '')),
            )
        except (ValueError, KeyError, TypeError):
            return None

class AuthorList(AuthorResource):
    """
    The list of authors (e.g. /api/authors/). 
    """
    def GET(self, request):
        return JSONResponse([self.format(a) for a in Author.objects.all()])

    def POST(self, request):
        author = self.parse(simplejson.loads(request.raw_post_data))
        if not author:
            return http.HttpResponseBadRequest()
        
        author.save()
        response = http.HttpResponse(status=201) # HTTP 201 Created
        response['Location'] = urlresolvers.reverse('api_author_detail', args=[author.pk])
        return response

class AuthorDetail(AuthorResource):
    """
    Author detail view (e.g. /api/authors/{id}/).
    """
    def GET(self, request, id):
        a = get_object_or_404(Author, pk=id)
        return JSONResponse(self.format(a, full=True))
        
    def DELETE(self, request, id):
        try:
            Author.objects.get(pk=id).delete()
        except Author.DoesNotExist:
            pass
        return redirect('api_author_list')
        
    def PUT(self, request, id):
        author = self.parse(simplejson.loads(request.raw_post_data))
        if not author:
            return http.HttpResponseBadRequest()
        author.id = id
        author.save()
        return JSONResponse(self.format(author, full=True))

class JSONResponse(http.HttpResponse):
    def __init__(self, data):
        indent = 2 if settings.DEBUG else None
        mime = ("text/javascript" if settings.DEBUG 
                                  else "application/json")
        super(JSONResponse, self).__init__(
            content = simplejson.dumps(data, indent=indent),
            mimetype = mime,
        )

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Please login first before commenting.