Creating an API With Django (Posted on March 2nd, 2013)
It's been a crazy week for me put I wanted to make sure I got some useful content out. This week I'll be jumping back to python/django and talking about decorators and how they can be used for creating an API.
A lot of people recommend Tastypie but I like to keep things simple when possible. Most APIs don't need anything as complex as Tastypie and just need something that can restrict access to some functions. Decorators make this really simple to do and I'm going to show you how.
In case you are unfamiliar with decorators they are functions that are called before a another function or method that allow you to do execute some code before the function is called. This can save lots of duplicated code and keeps your function code clean. A common use of decorators is the @login_required one in Django. You place this on top of all views that require a user to be logged in. This is essentially what will be replicating today. So without further ado here is the code to check for an API key in a request.
class required(object): def __init__(self, func): self.func = func def __call__(self, request, *args, **kwargs): key = request.POST.get('key', None) if key == None: return HttpResponseForbidden('Invalid or disabled app key') #Check if the user provided API key has access to the view being called try: APIKey.objects.get(key=key).allowed_functions.get(name=self.func.__name__) except APIKey.DoesNotExist: return HttpResponseForbidden('You do not have permission to access that API') return self.func(request, *args, **kwargs)
The function that is trying to be called passes itself as func to init. From there the decorator will automatically call the __call__ method. We pass in all the variables from the function that was called so that none of them are lost. The request variable will be in every view and will be needed to get the API key so we state it explicitly.
The logic for the decorator is just a few lines of code. Assume we have a model called APIKey which has a charfield for key and a many to many field for allowed functions. The first step is to check if the key was included in the POST request. If it was try and get the key from our APIKey table. Then access the many to many field and see if the function that is being called is in the allowed_functions table. You have access to the function name through self.func__name__. This is built in to python. If django doesn't throw an error then you can return the function and the function will proceed as normal.
Here's a quick example:
@required def hello_world(request): return HttpResponse("Hello World")
The hello_world function will only execute if the decorator call returns the function. Otherwise it will return one of HttpResponseForbidden messages.
Give it a shot and let me know if you have any questions or feedback.