Python Decorators for Djangonauts
If you’ve used Django for any amount of time then you’ve probably come across decorators. They’re the things with the @ signs over the top of your view.
The first one you’re likely to see is the @login_required decorator, which requires a user to be logged in before the view will run. If they are not, django will redirect to the login page.
If you’ve ever wondered what they do and how they work, I’m going to show you how to create your own.
What is a decorator?
A decorator is a function that wraps your decorated function (or view, as they’re functions too) in yet another function, allowing you to run code both before and after the main function.
A simple example
Allow me to explain. Let’s say we have a view, but we only want it to render if the HTTP_REFERER is www.example.com. If it’s not, then we want it to redirect to the home page.
So we have our view:
def my_view(request): .... return render_to_response('myview.html')
We’re going to call the decorator refer_check. The decorator looks like this.
from urlparse import urlparse from django.http import HttpResponseRedirect def refer_check(func): def decorate(request): referer = request.META.get('HTTP_REFERER', '') if referer == 'http://www.example.com': return func(request) else: return HttpResponseRedirect('/') return decorate
So how does this work? Well, our view (my_view) gets passed to the refer_check function – the func parameter is the view function.
We then create a new function, called decorate, which will wrap around our view function. That’s why it takes the request parameter, just like the view.
Now we get the HTTP_REFERER and check it. If it’s what we’re looking for, we run the view – the line where it says return func(request). If it’s the wrong referer value, we return and http redirect to the root of the domain.
Once all that has finished, we return the decorate function from the refer_check function.
To finish, we put the decorator above the view.
@refer_check def my_view(request): .... return render_to_response('myview.html')
In the above example, we did something before running the view. But you can also do things after the view has run.
Here’s a decorator that lets you time in seconds how long your function takes to run.
from datetime import datetime def time_it(func): def decorator(): a = datetime.now() func() b = datetime.now() print 'function: %s took %d seconds to run' % (func.__name__, (b-a).seconds) return decorator @time_it def myfunc(): for i in range(1,100000): print i
We have a function that prints the numbers 1 to 100,000. This is passed into our decorator which notes the time before the function is run. We then run the function using func(), and we note the time again. Then we print out a message with the function name and the number of seconds it took to complete.
As you can see from these examples, the decorators aren’t absolutely necessary. You could code both these examples by putting the code in the view/function itself. But it’s very useful if you like reusing your code, as you can apply your decorator to any function you like.