A little Django feature nobody tells you about

Recently i got involved in a new project which uses Django, so I started learning and using it. I won’t tell you here it merits as those have alread been described at lenght.

One of the features I like about Django are its pluggable applications: you can build a generic piece of code and that can easily be plugged in other’s sites to get some new functionality. Great examples of this are found in the django.contrib package, which include the already famous administrative interface, two comments systems, an authentication/authorization framework and more.

One problem you’ll soon find is that some of these are tied to be plugged at a fixed URL prefix. The auth system, for example, expects to be plugged in the /accounts/ directory and plugging it somewhere else leads to some more work for you. The easiest example is the @login_required decorator, which checks the user that is calling a view and, if it is not authenticated, it redirects to /accounts/login. If you have used a different URL prefix, you are forced to use the @user_passes_test decorator, which allows you to set the redirect URL.

Where is the problem? The problem is that those paths are explicitly hardocded in the views.

How to solve it? Dynamically build your URLs from your urlconf.

How do you do that? With django.core.urlresolvers.reverse()

That little boy helps you by giving you the URL path that would be called given a view and a set of parameters. Here is an example, suppose your urlconf has something like this:

urlpatterns = patterns('',
    (r'^/blog/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
        'dlog.blog.views.entry'),
)

you could create a function like this to get the URL of an entry:

    def get_absolute_url(self):
        return urlresolvers.reverse('dlog.blog.views.entry',
                kwargs={'year':self.pubdate.year,
                        'month':('%02d' % self.pubdate.month),
                        'day':('%02d' % self.pubdate.day),
                        'slug':self.slug})

when you then call an entry’s get_absolute_url() method, you’ll get the URL as urlconf expects it.

/blog/2007/01/06/your-slug-goes-here

I don’t know why this isn’t mentioned anywhere in the docs, I found it by hunting down the source code because I didn’t like hardcoding paths in my views.

I can understand it has some problems: it gives you only URLs relative to the root of the Django project, if you need an absolute URL or if you attached the site to a subdirectory of your domain, urlresolvers.reverse() is not enough, but I still think it should be advertised in the documentation.

One more thing, as everybody starting to use Django, I wrote a web log with it. But I haven’t replaced this one, I created a new one, but this time in Italian and that will be more personal and, hopefully, a real blog that I update it more often than monthly and I actually participate. So, if you can read Italian follow me on my new corner.