Code Kitchen: Refactoring Django views
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.
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: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(
'template.html',
{ 'y_dict':y_dict,
'z_dict':z_dict}, ...)
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'])
...
else:
filter = Filter()
return render_to_response(
'template.html',
{ 'y_dict':filter.get_y_dict(),
'z_dict':filter.get_z_dict()}, ...)
There's another twist to this refactoring that I'll tell you about in another episode of Code Kitchen.
[...] hinted, in the first Code Kitchen that there was a bit more to tell of the story of or recent Django [...]