Blue Flavor

Needle and EMP by Jeff Croft

Advanced Django template tags and filters

July 28th, 2008 at 3:51 p.m.

As a designer, I’m absolutely in love with Django’s template language. It gives me full control over my project’s markup and offers a lot of built-in tools that let me do complex things I haven’t been able to manage with other systems. And, in the case that it doesn’t give you what you need, you can always extend it by writing your own template tags and filters.

But buried deep within the bowels of the existing template tags and filters, Django has a few gems you may not know about, and a few others that can be used in ways you may not have thought of. I thought I’d take a moment to highlight a few of my favorite, more advanced Django template bits.

firstof

Django’s firstof tag is a very simple and very useful one that doesn't get much attention. All it does is display the first available variable from a list you provide. Take, for example, the typical "Welcome, Jeff!" message that you might see at the top of any site you've created an account on. On these types of sites, some people divulge their full name, some people only give up their first name, and some people aren't willing to provide any information. In this case, using the firstof tag can save you a lot of ugly if...then logic, by allowing you to do something like this:

<p>Welcome, {% firstof user.full_name user.first_name user.username %}!</p>

Simple, but very useful.

cycle

The cycle tag is very useful for many purposes, but it's especially handy when you're trying to style every other item differently. For example, maybe you want to add zebra stripes to your tables, or maybe you want every other comment after your blog entry to have a different background color. You can use cycle like so:

{% for row in table_data %}
    <tr class="{% cycle 'odd' 'even' %}">
        ...
    </tr>
{% endfor %}

This will result in every other table row having a class of “odd” or “even,” which you can style appropriately using CSS.

dictsort and dictsortreversed

Almost certainly the most fun Django filter to say out loud, dictsort lets you sort a list of objects by an attribute of that object. For example, let's say you have a list of people and each of them have a gender attribute of either "Male" or "Female". If you want to display all the women first and all the men second, you could do something like this:

<ul>
  {% for person in people_list|dictsort:"gender" %}
    <li>{{ person.full_name }}, {{ person.gender }}</li>
  {% endfor %}
</ul>

dictsortreversed works exactly the same way, but reverses the order after sorting (in this case, putting males ahead of females). You can dictsort on any type of attribute, including dates, integers, and strings.

regroup

The regroup tag is one that can be tricky to get your mind around, but is incredibly useful once you do. It regroups a list of items by a like attribute and saves them into a new variable. This variable has "groupers" that let you display the items by group. It's especially useful in combination with dictsort. For example, let's extend our gender list above. Maybe instead of saying "Male" or "Female" on every list item, we'd rather have two separate lists with a header above them stating the gender. Using regroup, we can do just that:

{% regroup people_list|dictsort:"gender" by gender as gender_list %}

{% for gender in gender_list %}
  <h3>{{ gender.grouper }}</h3>
  <ul>
    {% for person in gender.list %}
      <li>{{ person.full_name }}</li>
    {% endfor %}
  </ul>
{% endfor %}

To break that down, our people_list variable is being converted into a separate gender_list variable that contains (in this case) two items: one for male, and one for female. Each of those items has a grouper attribute (the word "Male" or "Female") and a list attribute (the list of people belonging to that group).

widthratio

According to the Django documentation, the widthratio tag, "calculates the ratio of a given value to a maximum value, and then applies that ratio to a constant." Unfortunately, I suck at math, so that doesn't make any sense to me. What does make sense is an example of how this tag can be used to create bar chats and the like. When I worked for the Lawrence Journal-World newspaper, we had daily polls for our readers and used the widthratio tag to display the results as a bar chart. For an example, see this poll. The template code used to create that poll looks something like this:

<table class="poll-results">
  <tr>
    <th>Response</th>
    <th></th>
    <th class="numeric">Percent</th>
    <th class="numeric">Votes</th>
  </tr>

  {% for choice in poll_choice_list %}
    <tr>
      <td>{{ choice.title }}</td>
      <td class="bar-graph">
        <div class="bar-wrapper">
          <div class="bar" style="width: {% widthratio choice.count poll.total_votes 220 %};"></div>
        </div>
      </td>
      <td class="numeric">{% widthratio choice.count poll.total_votes 100 %}%</td>
      <td class="numeric">{{ choice.count }}</td>
    </tr>
  {% endfor %}
</table>

Breaking this down, we pass the widthratio tag the number of times a choice has been voted for, the number of total people voting in the poll, and the number of pixels long each bar is (in this case, 220). In the second usage, we're trying to find the percentage, so we again use widthratio, but simply pass it 100 as the final argument.

Conclusion

These are just a few of the great tags and filters included with the Django distribution. There are many more, as well as hundreds of interesting template bits listed on Django Snippets. The wealth of tags and filters available for Django’s template language combine to make it an incredibly robust and useful tool for any meticulous designer.

What are your favorite Django template tricks?

Jeff Croft

More Information