The Digital Agency for International Development

Code Kitchen: Refactoring Django views

By Mark Skipper on 25 September 2010

We, at Aptivate, love openness, participation and learning. We're also into International Development and, often, we write software. Code Kitchen is a new idea to help us learn to become even better software developers using openness and participation.

Sushi Chef

The idea is that we make our programming practice transparent in the same way that a master Sushi Chef prepares your food right there in front of your eyes. This way you'll see how we think, get to offer suggestions (as comments) and maybe learn something yourself.

We're often too busy to do proper CodeKata so I'll start by highlighting some lessons I learned refactoring a customer project we're working on at the moment.

Refactoring a Django view

We're using Django to create a Web interface to some budgeting data stored in a tree structure like this:

[FocusArea]-*>[Objective]-*>[Activity]-*>[SubActivity]To keep the code simple, I'll pretend there are only three levels and call them X, Y and Z. Xs are the root elements, Zs are the leaves.

The tree view is rendered with a Django template. We send the template Python dictionaries {x:[y]} and {y:[z]} so it can re-create the hierarchy.  The tree view looked something like this:

def tree_view(request):
    y_dict = {}
    for y in Y.objects.filter(deleted=False):
        y_dict[y.x] = y_dict.get(y.x,[]) + [y]
    return render_to_response(
        { 'y_dict':y_dict,
          'z_dict':z_dict}, ...)
Not especially graceful, but easy on the eye.

Now comes the challenge. We have to filter this tree view to show X, Y and Z objects that match some criteria. The tree should show the matching objects together with all their parents, and their children too, but not their siblings (unless the sibling objects also match). There are going to be many different filter criteria and we don't want this to turn into a dog's breakfast of if and elifs.

The refactor started by creating classes for our filter criteria. The tree view instantiates the appropriate filter and uses it to fetch the dictionaries that the template needs:

def tree_view(request):
    if request.GET.has_key('country'):
        filter = CountryFilter(request.GET['country'])
        filter = Filter()

return render_to_response( 'template.html', { 'y_dict':filter.get_y_dict(), 'z_dict':filter.get_z_dict()}, ...)

Class Filter defines methods to match Xs, Ys and Zs against a default criterion (deleted=False) and its subclasses override them to define specific criteria. These are called by get_y_dict() methods which factor out the tasks of including the necessary parents and children of matched objects and preparing the dictionaries that convey the tree-structure to the template.

There's another twist to this refactoring that I'll tell you about in another episode of Code Kitchen.

Sept. 27, 2010, 2:59 p.m. - Code Kitchen: Overriding and keyword paramet...

[...] hinted, in the first Code Kitchen that there was a bit more to tell of the story of or recent Django [...]