Another thing that makes using Python pleasing is decorators. A decorator is a wrapper for a function (or method) that takes a function (or method) as an argument and returns a new function (or…) which is then bound to the name for the original function.
The newly-decorated function can then do things like checking the called arguments before invoking the original un-decorated function.
Django provides decorators for authentication so that you can wrap a view function with a check for client credentials before deciding whether to return the original response or a deny access.
In this manner Django’s authentication decorators encourage orthogonal code: the logic for displaying a view is separated from the logic for deciding whether you should be permitted to see the view’s output. By keeping them separate, it becomes simpler to re-use the authentication logic and apply it to other views.
Suppose you have a view that accepts a Django request object and checks whether the user is signed in:
def administration_page(request):
if request.user.is_authenticated():
return HttpResponse("Welcome, dear user.")
else:
return HttpResponseRedirect("/signin/")
With a decorator you can simplify and clarify things:
@login_required
def administration_page(request):
return HttpResponse("Welcome, dear user.")
For older versions of Python (pre 2.4) which don’t understand the @
operator one must explicitly decorate the view function like so:
def administration_page(request):
return HttpResponse("Welcome, dear administrator.")
administration_page = login_required(administration_page)
Note in the example that the original administration_page
function is passed to the decorator. The @
syntax in the first example makes that implicit but the two are equivalent.
The implementation of a decorator is interesting. It takes the function itself as an argument and returns a new function which does the actual checking. Here is how the decorator used above might do its stuff:
def login_required(view_function):
def decorated_function(request):
if request.user.is_authenticated():
return view_function(request)
else:
return HttpResponseRedirect("/signin/")
return decorated_function
The actual implementation of Django’s login_required
decorator is considerably less idiotic. Python’s functools module has helpers for writing well-behaved decorators.
Because functions in Python are themselves objects the decorator can accept a function reference, construct a new function that checks for authentication and then return a reference to that new function.
Simples!
(Simples gets less simples when you want to write a decorator that accepts configuration arguments because you then need either another layer of nested function definitions or a class whose instances can be called directly, but I’m going to ignore you for a bit and wow is that Concorde…?)