Django View Template Decorator

March 2, 2016 in Django


Recently I have been playing around with decorators in Python. Decorators are a really cool feature that allows you to add functionality to any function with a simple @decorator tag. It is very nice for injecting functionality to many functions that do similar things.

A place where this can be applied effectively is in Django views. This past weekend I was at a hackathon working with a fairly simple django website and decided to give view decorators a go. The idea is to decorate a view function with a tag like this: @template('index.html') in order to avoid having the return render('index.html', data) in every function. So here we go!

Here is our desired functionality. Let's assume for this sample that we have three simple pages that display users, groups, or both, as defined below. Notice, we assign the objects to a context dictionary. This dictionary is what will be passed to the template for rendering.

# app/views.py
from app.decorators import template
from app.models import User, Group


@template('index.html')
def index(request, context):
    context['users'] = User.objects.all()
    context['teams'] = Group.objects.all()


@template('users.html')
def users(request, context):
    context['users'] = User.objects.all()


@template('groups.html')
def groups(request, context):
    context['teams'] = Group.objects.all()

Now we can build the decorator to allow this functionality. We create a function called template which has a parameter called template which is what we will render. i.e. index.html. The inner function takes the view function as a parameter. Finally the third inner function is to actually set the request context and render the request. The call function also has a statement to see if the view function has a return value. If it does, it will return that, otherwise it will return the default rendering with the context.

# app/decorators.py
from django.shortcuts import render_to_response as render
from django.template import RequestContext


def template(template):
    def wrapper(view):
        def call(request, *args, **kwargs):
            context = {}
            ret = view(request, context, *args, **kwargs)
            if ret: return(ret)
            return(render(template, RequestContext(request, context)))
        return call
    return wrapper

I hope this is useful to you in your projects! I really like the concept of decorators and will surely be using them way more in the future. I believe they make for much more readable code.