Django Template Tricks
If you don’t need fancy javascript functionality, django templates can get a lot done. However, the language can seem quite inexpressive at first glance. You might find yourself repeating markup, or creating custom template-tags for silly single purposes. In this post we’ll go over a couple of less known features of django templates. These will enable us to write simpler views and fewer template tags.
The as
keyword
The as keyword allows you to assign expressions to variables. It’s even more expressive than plain template expressions, because it allows you to process data returned from tags, not just filters. If you ever find yourself wishing filters could take two arguments, or wanting to process data through multiples tags, this is the way to go.
This is a documented trick, this example is taken straight from the django docs:
{% url 'some-url-name' arg arg2 as the_url %}
<a href="{{ the_url }}">I'm linking to {{ the_url }}</a>
The scope of the variable created by the
as var
syntax is the{% block %}
in which the{% url %}
tag appears.
If you often deal with deeply nested structures, you may find it useful to create an alias
helper tag just to help you create extra variables.
#templatetags.py
@register.simple_tag
def alias(v):
return v
{% alias my.deeply.nested.path as foo %}
<p> {{foo.attr1}}, {{foo.attr2}} </p>
block.super
If you know about blocks, you’ve probably used them to override the behaviour of certain area of pages. A really common pattern is to have all your templates extend a base.html
that sets up common regions, like a header, content and footer. Pages will typically override the content blocks, but keep the footers and headers as-is.
What you might not know is that you can also render the parent template’s block and extra content. I mostly use it to add special page-specific alerts.
{% extends base.html %}
{% block header %}
{{block.super}}
{% alert type="warning" %}
Be careful! This page is special
{% endalert %}
{% endblock %}
{% block content %}
...
{% endblock %}
One more thing: don’t be afraid of too many files! Overriding parent template blocks work best when you have many small templates. You may find the idea of an “abstract” template (a template that must be extended) useful.
I recommend following a naming convention between your views, templates and url-names. Most urls and views are one-to-one and have one template, so it’s easy to have them match similar names.
using blocks inside loops
This tip is much less widely applicable, but I’m including it simply because it’s mind-blowing. It’s not a trick that comes to mind often, but it has helped me D.R.Y out a few similar tables, lists and nested-list layouts
Imagine you have many tables that all show book information. Different tables show different columns, but they all show the book name and author. Instead of repeating the book name and author columns accross all your tables, you can put those in a block. Extending the block will keep the same
<table>
<thead>
{% block table_headers %}
<th> Author </th>
<th> Title </ht>
{% endblock %}
</thead>
<tbody>
{% for book in books %}
<tr>
{% block table_row %}
<td>{{book.author}}</td>
<td>{{book.title}}</td>
{% endblock %}
</tr>
</tbody>
</table>
Now you can create many smaller templates that render this table, but with extra markup:
{% extends "base-book-table.html" %}
{% block table_headers %}
{{block.super}}
<th> Sales last year </th>
<th> Profit last year </ht>
{% endblock %}
{% block table_row %}
{{block.super}}
<td>{{book.sales_last_year}}</td>
<td>{{book.revenue_last_year}}</td>
{% endblock %}
Our second template’s block-override inherits the parent template’s scope, and has access to the book
for-loop variable.
To be honest, you’ll probably never use this trick. It’s rarely the case that you have complex iterated-layout logic that is worth repeating like this.
block arguments (external library)
Disclaimer: I wrote this library
django-template-block-args is a tiny tool that helps componentize repetitive bits of templates. Let’s say our website is heavily card-based, then we might have a lot of templates that repeat something like:
<div class="card">
<div class="card-header">
title
</div>
<div class="body">
body text
</div>
</div>
To avoid repetition, you might think we can create a template-tag and instead write something like
{% card header="title" body="body text" %}
But this falls flat when you need multiple lines in your card body, or markup in the body, or the ability to call other tags like translation helpers. django-template-block-args enables you to create template tags that can take entire blocks as arguments:
{% card "success" %}
{% blockarg 'card_body' %}
{% translate "I'm a card-header!" %}
{% endblockarg %}
{% blockarg 'card_header' %}
I'm a card-body
<div> with multiple lines </div>
{% endblockarg %}
{% endcard %}
Implications for patterns
These tips allow you to be more of a purist in how you separate concerns and apply the model-view-template pattern. The more flexibility your templates have, the less your views need to concern themselves with presentational details.
- The
as
keyword reduces bloat in your view contexts and obviates the need for many custom template-tags. {{block.super}}
will encourage you to design views around extendable layouts- django-template-block-args helps you componentize markup in a flexible way
Any good django developer will know that views should be “dumb” and that business logic should be offloaded to models, forms, or some kind of query/service layer. Presentational logic, on the other hand, is often forgotten.