Django's wonderful permalink decorator

November 29th, 2010

First a quick disclaimer: I want to make clear that I am by no means an expert on Django. I think it is an awesome web development framework, using an awesome programming language, but I have only been using it off and on for a couple of years now and consider myself still a newbie. So what I'm talking about here is probably blatantly obvious to the seasoned Django developer, but since it was a new insight for me, I'm posting it here in case it might be of benefit to other Django newbies like me. :) So if you see something that needs correcting or could be done in a better way, please don't hesitate to add your insights in a comment.

With that out of the way, let's get down to business.

Everyone familiar with Django probably knows (or should know) about the permalink decorator. It provides a great way to tie your web app's model views to links you may create for them in your templates, without having to worry about the actual URL's involved (other than in your urls.py of course).

I had been using them as described in the tutorials, to create 'get_absolute_url' methods for my models like this:

from django.db.models import *

class Something(Model):
    slug = CharField(max_length=50)
    title = CharField(max_length=100)
    text = TextField(blank=True)

    @permalink
    def get_absolute_url(self):
        return ('views.view_something', (), {'slug': self.slug})


This works great provided there is a url declaration in your urls.py pointing to a 'view_something' function that takes a slug argument and you defined that 'view_something' view function. In some template you can now create a link to a specific 'something' object by doing:

<a href="{{ something.get_absolute_url }}">{{ something.title }}</a>


This is all pretty basic stuff, and explaining it is not the point here. If you don't get what I'm talking about, I suggest you read the Django tutorial first.

Being a newbie, and not understanding how Django does all of its magic in the background, I thought for quite a while that there was something special about the method name 'get_absolute_url'. It turns out there isn't. As far as I have been able to determine, it seems the only special thing about it is that if you use the Django admin infrastructure, specifying a 'get_absolute_url' function for your model gives you a 'View on site' link in the model's admin page, which is pretty cool. But other than that, you could use any old name you wanted for this function (not that I recommend doing that, using the standard name adds clarity and that's a good thing). What is special though, is that you put the @permalink decorator in front of the method, whatever you end up calling it. This decorator is what provides the magic for looking up the specified view function and constructing a correct URL for the object based on the information found in urls.py.

What was a new insight to me (and probably was always clear to most people) is that you can have as many methods with a @permalink decorator as you like for a model. This provides a convenient way to transparently create links for many different ways of 'view'ing your model. Let's say you have a model for data you want to display using a normal HTML page, and you have a view to edit the model data, and you have a view for a machine interface that generates JSON data from your model, and you have a view that generates partial HTML you load into a page using AJAX. You could create a model like this:

from django.db.models import *

class Something(Model):
    slug = CharField(max_length=50)
    title = CharField(max_length=100)
    text = TextField(blank=True)

    @permalink
    def get_absolute_url(self):
        return ('views.view_something', (), {'slug': self.slug})
    @permalink
    def get_edit_url(self):
        return ('views.view_something_edit', (), {'slug': self.slug})
    @permalink
    def get_json_url(self):
        return ('views.view_something_json', (), {'slug': self.slug})
    @permalink
    def get_html_chunk_url(self):
        return ('views.view_something_ajax', (), {'slug': self.slug})


After you implement the view functions to spit out the appropriate data and tying them to URL's, you can use template code like this and get links to pages to view and edit objects using this model:

<h1>{{ something.title }}</h1>
<ul>
<li><a href="{{ something.get_absolute_url }}">View</a></li>
<li><a href="{{ something.get_edit_url }}">Edit</a></li>
</ul>


Or you could generate JavaScript to get to your raw data this way (using jQuery):

$.getJSON('{{ something.get_json_url }}', function(something) {
    // The variable 'something' now received your data
});


Or if you dynamically want to update a div on your page with id 'something', you could generate this piece of JavaScript to do it (again using jQuery):

$('#something').load('{{ something.get_html_chunk_url }}');


The cool thing is that you do not need to worry at all about what the actual URL is for any of these examples. If you ever want to change your URL layout, you only need to edit your urls.py and you don't have to worry about updating URL's all over the place, which is great.

 
 
© 2010 Patrick Van Oosterwijck.

Using technoblog blog engine by Will McGugan