Brandon Konkle
Brandon Konkle

Principal Engineer, type system nerd, Rust enthusiast, supporter of social justice, loving husband & father, avid comic & manga reader, 日本語を勉強してる。

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.

Share


Tags


Django's Dynamic URLs

One of the strengths of the Django framework is its emphasis on the Don't Repeat Yourself philosophy. From Wikipedia:

"The philosophy emphasizes that information should not be duplicated, because duplication increases the difficulty of change, may decrease clarity, and leads to opportunities for inconsistency."

This approach has many advantages in web development, avoiding a great deal of the redundant and tedious HTML editing that often must be done on static sites. One way that Django truly shines in facilitating the implementation of DRY is in its URL handling. In traditional static HTML authoring, the URL is defined by the filesystem. If a directory changes for any reason, every HTML file that contains a link to that resource must be edited with the new URL. Even in small sites, this can quickly eat up a developer's time and generate a lot of frustration.

It's easy to fall into the trap of handling Django sites the same way, hardcoding links to views in templates and models. After creating URLs in your urls.py file, it's tempting to simply add links in your template to "/my/url/" and absolute URL methods in your models.py that look like return '/%s/%s/' % (self.attribute, self.other_attribute). If you ever want to change the URL, however, the pain begins. You must go through each template and model to hand-change all of the links you included in your code. Say hello to hours of mindless manual labor!

Luckily for us, Django provides some excellent tools to completely avoid that kind of tedious nightmare and make our URLs DRY compliant throughout our applications. The first and most commonly used of these tools is the @models.permalink decorator that can be used in your models.py files. This is covered in Django's excellent documentation site here. This works very well in conjuction with named URL patterns, which is how I will be using it in the examples below.

The @models.permalink decorator allows you to reference a view as the target for a link generation method (such as get_absolute_url) in your models. Django automatically determines the URL to the view based on your current urls.py, and if your URL patterns change the link generated by this method changes with them.

Examples are often the best way to describe a concept, so I'm going to jump into a demonstration of this in a quiz application I recently worked on. My URL to a view of a quiz model looked like this:

url(r'^(?P<category>[w-]+)/(?P<date>[d-]+)/$',
    'quiz', name='quiz'),

In my quiz model, I have the following method to generate an absolute URL to the quiz:

@models.permalink
def get_absolute_url(self):
    return ('quiz', [str(self.category.slug),
                     self.created.strftime('%m-%d-%Y')])

In this example, self.category is a ForeignKey pointing to a category object. I passed positional parameters in this example, but if you'd like you can pass keyword parameters as well. Details are in the aforementioned documentation entry.

Once you've created your permalink, you can take advantage of it in your templates whenever you need to link to the standard view of a quiz item. You can simply call the item's method in your template like this:

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

You can handle other scenarios with multiple permalinks in your model. For example, in the same application I also created a permalink to the results view for a quiz.

@models.permalink
def get_results_url(self):
    return ('results_quiz', [str(self.category.slug),
                             self.created.strftime('%m-%d-%Y')])

I call this in the template the same way.

<a href="{{ quiz.get_results_url }}">{{ quiz.title }}</a>

The URL Template Tag

Permalinks should be able to handle the majority of the links in your site. There are cases, however, where this will not meet your needs. This is most commonly encountered in your site navigational menus. Often you will want to have a link that simply goes to the index view for a particular app. In many cases, there won't be a way to accomplish this through model permalinks. Django comes to the rescue here with a template tag for generating absolute URLs based on views, documented here.

In my main navigation for the site housing the quiz app, I have a link that looks like this:

<a href="{% url quiz_index %}">Quizzes</a>

This generates a link to the desired view, using the URL pattern named 'quiz_index'.

This tag has some limitations however, which Jeff Croft illustrated in a post here. It's definitely a good idea to carefully consider each usage of this tag, and only utilize it when necessary.

The reverse() Utility Function

If you need functionality similar to the URL template tag in your views, you can use the reverse() function, documented here. I've not had the opportunity to use this function much so I don't have an example here, but it is another tool that Django provides to make dynamic URL schemes possible.

Hopefully this overview has been helpful in learning how to put the DRY philosophy to work for you using Django. If you have suggestions or questions, please feel free to leave a comment below.

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.

View Comments