Crowley Code! 
 (Take 12)

Django 2008/12/15

I asked Malone the other week why Django made me go all return render_to_response('foo.html') in my view named foo instead of assuming some defaults. To be explicit, candidates for those defaults might go something along the lines of:

On top of that, I asked him why Django didn't figure out what response format makes sense, given the URL and headers in the request and the formats available for the template chosen.  His response was basically that Django hates magic.  If it did all of these things, it'd favor convention over configuration and be called Rails.  Or Pails.

After playing around with Django for a few days and now starting to use it for a real project, I'm seeing the wisdom in Django's (and indeed, Python's) choice to avoid magic.  I've already used the word "explicit" once in this post and that's what this is really all about.

Additionally, Ruby seems to skew towards implicitness in the language, while Python skews toward explicitness. I like explicitness. — Joe Stump

I'm not here to start a Ruby-versus-Python or Rails-versus-Django fight. I'm here to talk about being explicit with code.

My efforts a while back hacking the Ruby interpreter were a bit misguided.  I was heavily and prematurely optimizing my desired use case at the expense of code clarity and at least seven other traits generally considered to be positive.  In the resulting Frankenruby interpreter, @foo could show up in a file outside of a class with no warning, meaning that file could only ever be used by my very streamlined URL-to-code routing script.  In retrospect, it's a good thing I never got it to work just right.

The desire to remove as many declarative, housekeeping-esque lines of code is seemingly noble but it is at odds with another principle of mine, learned from years in the trenches writing C(++): always #include everything a file uses.  This makes files instantly more portable and less magical.  Despite Django's mascot being a magical pony, I gather that it, too, avoids magic in most scenarios.

Being explicit allows Django to be loosely-coupled.  Imagine trying to remove the Django ORM or templating system if they were omnipresent in every Django app from start to finish.  Tedious.  As it is, a single "#" sets you free.  Even the concept of having many apps within a project enables further explicit use or disuse of each package.

Being explicit also lowers the barriers-to-entry for would-be framework hackers like myself.  My first serious afternoon saw me trying to create a fairly involved template tag.  It would have made page titles work just about like the excellent Headliner plugin for Rails.  I discovered along the way that the {% block foo %} template tag, if placed within another block, would both override the named block and output its contents in place.  Observe, here's how I decided to do page titles in Django:

In templates/layout.html:

<title>Foo — {% block title %}{% endblock %}</title>

In other templates:

{% extends 'layout.html' %}
{% block title %}Only shows up in the title tag{% endblock %}
{% block content %}
<p>foo</p>
{% endblock %}
{% extends 'layout.html' %}
{% block content %}
<h1>{% block title %}Shows up here and in the title tag{% endblock %}</h1>
{% endblock %}

The ORM looks similarly easy to figure out though I haven't taken the plunge yet.  I already know what I'm going to build to learn it, though: a command-line SQL tool that works through a combination of Python and hard links.  The python script will find the closest Django settings module and use the database connection info there.  The command will be invoked by typing out a SQL statement on the command line.  The SQL statement will be ' '.join(sys.argv) and the commands SELECT, INSERT, etc. will be routed to the Python script using hard links.  I don't know if this is a good idea but I know it'll be relatively easy to do thanks to how easy it seems to be to dive into the Django internals.

Comments (1)

  1. I think part of the reason Django can get away with avoiding magic like this is because of the dynamism of Python. For example, a simple function decorator is sufficient to duplicate the view rendering behavior you're missing from Rails. Here's my 5 minute implementation (minus error handling or any sort of testing):

    from django.shortcuts import render_to_response
    from functools import wraps
    def default_template(view_func):
        @wraps(view_func)
        def _wrapper(*args, **kwargs):
            result = view_func(*args, **kwargs)
            if result is None:
                return render_to_response('%s.html' % func.__name__)
            elif hasattr(result, 'items'):
                # result is a dictionary
                return render_to_response('%s.html' % func.__name__, result)
            elif isinstance(result, basestring):
                # result is a strong
                return render_to_response(result)
            elif hasattr(result, '__iter__') and len(result) == 2:
                # result is an iterable with two elements
                return render_to_response(result[0], result[1])
            return result
        return _wrapper

    It'd be equally easy to write a decorator that chooses a template based on extension. In fact that's exactly how I tend to do data serialization for APIs written in django. The view returns a dictionary (or a subclass of some object I know how to serialize) and then I look at the extension and serialize to whatever format is desired. That way I'm not duplicating the logic to choose serialization format in every view function. To accomplish this I usually require that view functions have a kwarg called extension that's parsed out of the request URL by the URL handler logic. The nice thing is that I'm imposing this convention on myself, which gives me lots of flexibility. The downside is that I have to write a bit more code (or find it on djangosnippets.org).

    Mike Malone — 2009/01/20 2:30 pm

Richard Crowley?  Kentuckian engineer who cooks and eats in between bicycling and beering.

I blog mostly about programming and databases.  Browse by month or tag.

To blame for...


© 2009 Richard Crowley.  Managed by Bashpress.