An Architecture for Django Templates

I’m going to share the structure I use for templates when building a Django application — either from the ground up or when restructuring an existing application. The process is geared towards reusable apps, but the concepts involved are flexible. This is a framework, of sorts, and a style guide.

This structure evolved organically for me over thousands of hours developing templates and it works well for me. Your mileage may vary.

And, please, you are already organizing templates into app-specific subdirectories, right? Right?

Before we start, I’ll share the structure of one of my basic Django apps, with templates. Gazette is a Django blogging app (yes, another one) that I’ve been working on, and which I will mine for examples throughout this article.

gazette/
    __init__.py
    models.py
    views.py
    static/
        ...
    templates/
        gazette/
            __base.html
            __l_single_col.html
            __l_right_sidebar.html
            __l_left_sidebar.html
            _article_full.html
            _article_summary.html
            _author_list.html
            _author_name.html
            _category_list.html
            _navigation.html
            _tag_list.html
            article_detail.html
            article_list.html
            author_list.html
            category_list.html
            tag_list.html

Know Your Templates

The first principle of my template development process comes straight from The Zen of Python:

Readability counts.

What this means in template development is that I should be able to glance at my template directory and instantly know what purpose each template serves. More importantly, when a client comes to me with a page in their application that requires an update, I shouldn’t have to hunt for which file contains the relevant code. It should require scanning two templates at most.

To this end, I organize all of my templates into three distinct categories: extendable templates, includable templates, and page templates.

Extendable Templates

Extendable templates form the basic structure of your HTML pages. They are templates whose specific purpose is to be extended by other templates with the {% extends %} tag. To keep from conflating my extendables with my page templates I adhere strictly to never calling an extendable directly from a view and never extending a template which is not an extendable. By way of naming convention, I start all my extendable templates with double-underscores, i.e., __base.html.

The most important template is __base.html which is the HTML scaffolding that all of your pages should contain. Generally this template should be pretty minimal. Mine almost always looks like something like this:

{% load staticfiles i18n %}
<!DOCTYPE html>

<html>
    <head>
        {% block meta_tags %}{% endblock %}
        <title>
            {% block title %}{% trans "Django Gazette" %}{% endblock %}
        </title>
        {% block stylesheets %}
            <link rel="stylesheet" href="{% static "gazette/bootstrap/css/bootstrap.min.css" %}" type="text/css" />
            <link rel="stylesheet" href="{% static "gazette/css/base.css" %}" type="text/css" />
        {% endblock %}
        {% block javascript %}
            <script src="{% static "gazette/js/jquery-1.7.2.min.js" %}" type="text/javascript"></script>
            <script src="{% static "gazette/bootstrap/js/bootstrap.min.js" %}" type="text/javascript"></script>
        {% endblock %}
        {% block extra_head %}{% endblock %}
    </head>
    <body>
        <header class="navbar navbar-fixed-top">
            {% include "gazette/_navigation.html" %}
        </header>
        <div id="main" role="main">
            <div class="container">
                {% block content %}
                {% endblock %}
            </div>
        </div>
    </body>
</html>

As you can see, I use Bootstrap when I’m starting a new project, but this approach is not Bootstrap-specific.

When I write this base template, I like to code defensively. I think to myself, “If another developer (including myself in six months) comes to this project and needs to build a new template, how can I ensure that they can create any template they want, without having to change the base template.” The answer turns out to be through the use of plenty of obviously-named {% block %} tags to override and extend as necessary. I’ll talk more about {% block %} tags later on.

At this point, if your app is very simple, you may not need much more than the __base.html template to provide your structure. You can move straight on to writing your page templates and includables. But if your app is particularly complex, you may want to build out a more thorough framework with some layout templates.

In Gazette, I use three layout templates, whose names I start with __l_ (again, so I instantly know their purpose): __l_single_col.html, __l_right_sidebar.html, __l_left_sidebar.html. I stash these in the same directory with the other templates, in accordance with the Zen of Python:

Flat is better than nested.

However, if you need many more templates than three, you may want to create a layouts/ subdirectory. When I worked on a project with a particularly complex grid, I created just such a subdirectory and filled it with templates such as layouts/100.html, layouts/25_75.html, layouts/50_50.html, &c.

Here’s an example of one of my layouts, __l_right_sidebar.html:

{% extends "gazette/__base.html" %}

{% block content %}
    <div class="row">
        <div class="span8">
            {% block main_col %}{% endblock %}
        </div>
        <div class="span4">
            {% block side_col %}{% endblock %}
        </div>
    </div>
{% endblock %}

It’s a very simple template — so simple that you might feel it’s unnecessary, but, believe me, this thin layer provides a lot of advantages when you’re building out your page templates later.

Includable Templates

Includable templates are templates that you intend to be included in page templates using the {% include %} tag. As with the extendables, I am strict in mandating that includables adhere to their purpose. You can also think of them as the building blocks that make up your site, glued together by your page templates. There are two primary reasons to make a chunk of your site includable:

  1. because you are going to reuse that particular module on multiple pages and you want to keep your template code DRY.
  2. because you want another developer to easily override a particular module in your app without having to gut your template structure entirely.

As always, code defensively: be prepared for other developers (including yourself in six months) to want to modify and override your templates in ways you never dreamed of.

And keep an eye on your balance. Too few includables will make small modifications infuriating to carry out, but too many will create spaghetti code that’s tough to browse.

Page Templates

Page templates are the meat of your app, but if you’ve built up some good extendables and maybe even includables before you get here, you’ll find them pretty easy to put together. They’re really just the glue that holds all your templates together.

Your page templates are the ones that your views will call directly. They link your extendables and your includables together in synchronous harmony. For example, this is article_detail.html:

{% extends "gazette/__l_right_sidebar.html" %}

{% block title %}{{ article.title }} | {{ block.super }}{% endblock %}

{% block main_col %}
    {% include "gazette/_article_full.html" %}
{% endblock %}

{% block side_col %}
    {% include "gazette/_tag_list.html" with tags=article.tags.all %}
    {% include "gazette/_category_list.html" with categories=article.categories.all %}
{% endblock %}

As you can see, this page uses the __l_right_sidebar.html layout. The main column contains a full article. The side column contains both a tag list and a category list. It’s just about as easy to read as a template can be.

I promised you the layer of layout separation would lead to increased awesomeness later, and it does: if I decide that I’d rather have the tags and categories on the left side of the page, all I have to do is change the first line of article_detail.html to:

{% extends "gazette/__l_left_sidebar.html" %}

and the content will all fall into place, right where I want it to.

Better still, if another developer is overriding my templates and they prefer, say, 75% - 25% layouts to the 66% - 33% layout I wrote into __l_right_sidebar.html, then they can override that template to:

{% extends "gazette/__base.html" %}

{% block content %}
    <div class="row">
        <div class="span9">{# this is now 75% of the page #}
            {% block main_col %}{% endblock %}
        </div>
        <div class="span3">{# this is now 25% of the page #}
            {% block side_col %}{% endblock %}
        </div>
    </div>
{% endblock %}

and all the pages that use that layout will follow suit.

As far as naming convention is concerned, when appropriate, I try to follow the naming conventions that Django’s generic views use, e.g., <object_type>_list.html, <object_type>_detail.html, <object_type>_form.html, &c. If the template you’re writing doesn’t easily fit into that categorization, just try to keep it fairly obvious which views it corresponds to when naming it.

Some Notes on {% block %} and {{ block.super }}

Django’s template inheritance system is wonderfully powerful, and it’s easy to get carried away. I often see structures in base (i.e., extendable) templates where developers place default content in their blocks:

{% block main %}
    This is content for the front page of my application.
{% endblock %}

presumably with the intention of overriding that content when extending that template for use on other pages. I encourage you not to buy into this structure. It conflates your extendables with your pages and makes it harder to know where to look when updating particular template content. (“Is it in frontpage.html or is it in base.html?”) Instead, think of the block content in your extendables as content that you may want to reuse on various page templates with {{ block.super }}.

The simplest example of this is the <title> element. I often see structures like this in base templates:

<title>{% block page_title %}{% endblock %} Site Title</title>

This not only strikes me as inelegant (Do we put the separator that goes between the page title and the site title — e.g., “Page Title | Site Title” — in the base template or in page templates? What about pages that don’t have page titles?), but limits what a developer can do without modifying the base template. I’d much rather see a simple structure like this in your base templates:

<title>{% block title %}Site Title{% endblock %}</title>

Similarly with CSS or other assets, rather than

<link rel="stylesheet" type="text/css" href="base.css" />
{% block styles %}
{% endblock %}

or, heaven forbid,

{% block styles %}
    <link rel="stylesheet" type="text/css" href="base.css" />
{% endblock %}
{% block extra_styles %}
{% endblock %}

This is convoluted and unnecessary, when a simple

{% block styles %}
    <link rel="stylesheet" type="text/css" href="base.css" />
{% endblock %}

will suffice. Later, in your page template, you can add styles with:

{% block styles %}
    {{ block.super }}
    <link rel="stylesheet" type="text/css" href="specific_page.css" />
{% endblock %}

Similarly, with the title you can:

<title>{% block title %}Specific Page | {{ block.super }}{% endblock %}</title>

This, to my eyes at least, is more elegant — the blocks are better named, there are fewer of them, they more accurately express the thought behind the structure — and if you want to abandon the default styles or default title for a specific page, without abandoning your base template altogether, you still have the freedom to do so.

{{ block.super }} is a powerful tool. Use it responsibly.

A Few Final Notes

Internationalization

Especially if you’re writing templates for a reusable app, please internationalize your templates. It’s so gloriously easy with the {% trans %} and {% blocktrans %} tags.

{% include %} and {% with %}

For a really long time I was still writing terrible code like:

{% with article.categories.all as categories %}
    {% include "gazette/_category_list.html" %}
{% endwith %}

Until I realized that this superior syntax has been available since Django 1.3:

{% include "gazette/_category_list.html" with categories=article.categories.all %}

For extra bonus encapsulation, try giving your includables exclusive context:

{% include "gazette/_category_list.html" with categories=article.categories.all only %}

Closing Tags

HTML and Django Template Language are, by nature, nested, which sometimes impairs legibility, especially when dealing with long blocks. Django Template Language offers this useful optional syntax for keeping track of your blocks:

{% block camelot %}
    Pretend this is so much content that
    you lose track of what block you're
    in before the end.
{% endblock camelot %}

But what about other cases? I often use template comments to mimic this structure for other HTML and template tags:

<div class="article-content">
    A very long article.
</div>{# /.article-content #}

which I find greatly improves the legibility of my templates.

Treat HTML and Template Tags Identically

I used to believe that indentation of HTML tags should be treated separately from the indentation of template tags — perhaps out of a misguided belief that the indentation of outputted HTML actually mattered — which led to a lot of code like this:

{% block navigation %}
<nav id="navigation">
{% with bookpage.book.pages.all as bookpages %}
    <ul class="book-page-list">
    {% for page in bookpages %}
    {% if page != bookpage %}
        <li><a href="{{ page.get_absolute_url }}">{{ page.name }}</a></li>
    {% else %}
        <li class="active">{{ page.name }}</li>
    {% endif %}
    {% endfor %}
    </ul>
{% endwith %}
</nav>
{% endblock %}

Which, as it turns out is a lot less legible than what you get if you just treat template and HTML tags as equal citizens:

{% block navigation %}
    <nav id="navigation">
        {% with bookpage.book.pages.all as bookpages %}
            <ul class="book-page-list">
                {% for page in bookpages %}
                    {% if page != bookpage %}
                        <li><a href="{{ page.get_absolute_url }}">{{ page.name }}</a></li>
                    {% else %}
                        <li class="active">{{ page.name }}</li>
                    {% endif %}
                {% endfor %}
            </ul>
        {% endwith %}
    </nav>
{% endblock %}

And there’s no good reason not to.

What else?

This structure evolved organically for me and continues to evolve. I’ll keep you abreast of further ideas for template architecture. What about you? Do you have particular tips and tricks for keeping templates organized and legible?

Add Your Comments

To prevent spam, comments will be sent for approval before they appear on the website. Sign in on OnCampus to comment immediately.

Sounds good to me.

Sometimes I think there should be an HTML Tidy middleware so we can have our cake (legible templates) and eat it too (legible output).

Maybe not, though.

Joseph Spiros

Thanks for putting this all in one post – it’s a great reference and has been bookmarked.

(Also, you used to not indent templatetags like HTML?)

Kriti

Also, is this blog gazette-powered?

Kriti

Kriti:

In my defense, the Django Tutorial does not indent template tags either. Probably a practice which should be rectified.

Gazette is still very much in-development and isn’t currently running any public sites. This blog is still Philo-powered. I’ll let you know when Gazette is in use, though!

Harris Lapiroff

When it comes to structuring template directories, I’m not convinced that “flat is better than nested” applies. File systems are inherently tree structures, and I think it’s good to take advantage of that.

The django admin (whatever else may be said of it) does so when looking for templates: first <app>/<model>/change_list.html, then <app>/change_list.html, then change_list.html. (Rather than _app_model_change_list.html etc.)

Flat template directories lead to Smurf template naming; nested directories let us organize related templates neatly. To take the argument ad absurdum - if flat template directories were clearly superior, then templates could simply be organized by prefixing the template name with the app name (instead of using app directories.) _gazette_article_full.html sound fun? Perhaps the more applicable line of the Zen of Python is:

Namespaces are one honking great idea – let’s do more of those!

Using nested directories, gazette’s template structure could look like this:

gazette/
    article/
        _full.html
        _summary.html
        detail.html
        list.html
    author/
        _list.html
        _name.html
        list.html
    category/
        _list.html
        list.html
    layouts/
        __single_col.html
        __right_sidebar.html
        __left_sidebar.html
    tags/
        _list.html
        list.html
    __base.html
    _navigation.html

I find this kind of structure much easier to parse and use.

Stephen Burrows

Stephen: I’ve definitely considered structures like this and may consider them again, but at this point I’ve ruled against them, for a few reasons:

  1. an app’s template directory is already buried at least two levels deep within the app. Burying the bulk of the templates three levels deep feels like one step too far to me.
  2. in my own experience, I still find it very difficult to browse structures like this, especially since directories at that level would serve a number of different purposes, so it would not be immediately obvious, for instance, whether layouts/ was a collection of templates for a Layout model, templates for some type of “layout” functionality within your app, or layout templates that I’ve described in this blog post. In your example, the layouts/ directory is not at analogous to the authors/ directory — but there’s no way to know that without opening both directories and looking at the templates. And it’s easy to imagine littering that folder with many differently-purposed subdirectories.
  3. most models (at least in simple apps) will not have many more than three related templates, and I feel that directories with fewer than three files in them are not really justified in adding an extra level of nesting.

However, I can also see the structural benefits for complex apps, and may reconsider it in the future.

Harris Lapiroff

I’m curious as to your thoughts these days about bootstrap vs. h5bp.

Stephen Burrows

Stephen: I love both Bootstrap and H5BP and I certainly don’t think of them as mutually exclusive.

I think Bootstrap is an amazing library of very common components, which means writing less boilerplate CSS code on your own. I’m especially appreciative of the way it normalizes the size of form controls and buttons—a task I’ve always found especially onerous—and the sane default typographic styles. I don’t particularly like seeing people using it without adding their own styles to it though, which is a danger of a framework that has such a strong and solid aesthetic of its own. But it’s a foundation, not a theme, and developer-designers would do well to remember that.

H5BP is also great, though I use it more as code-inspiration than actually downloading and using the framework. I find that it comes with a lot of stuff designed for developing for older browsers that—in most of my projects—I don’t actually care about too much. When possible, I tend to be pretty aggressive about supporting only modern browsers.

Harris Lapiroff

Thanks for the post, it’s interesting to hear your experienced opinions on template usage.

It makes me wonder how you deal with keeping js that is related to includable templates together with the template. Let me explain:

Suppose you have some re-used widget, say a voting widget to up/downvote posts. So you put the html for that widget in mysite/templates/_vote_widget.html. Of course, there is some js to go with that widget, to manage its behavior and state. Often this implies including some js file in the head, or bottom of your page:

It seems that the include should pull any needed javascript, but django doesn’t seem to support this.

Naturally, you’ll have some script in the template itself, which is fine and good. But for something like the voting_widget.js file in the example above, the js should only be included once in the head (or bottom) of the page.

Do you also find yourself wanting to do this, and how do you manage it?

It seems to be common enough, since there are a few attempts at extending the template language to serve this use case: - http://django-sekizai.readthedocs.org/en/latest/ - https://github.com/yunmanger1/django-insert-above

I’m on the verge of trying to write one myself! But it doesn’t seem like that should be necessary!

Edward Newell