Create reusable Django templates

Problem

You want to create reusable Django templates to avoid declaring common elements (e.g. header, footer, menus) on all Django templates and have the ability to override section content on a page by page basis.

Solution

Use Django's built-in tags: {% block %}, {% extends %} and {% include %}.

How it works

All websites have common sections that are used across sets of web pages. For example, the header and footer sections on almost all website pages are identical, whether a website has 5 or 100 pages. Other sections of a website are commonly based on topic, such as using specific menus for certain sections. As illustrated in past recipes, each Django template represents a web page. However, this approach to use individual Django templates for each web page can make a web site's pages difficult to manage.

With reusable Django templates you can define common sections on separate templates and reuse them inside other templates. This process makes it easy to create and manage a web site's pages because a single template update takes effect on all templates. Reusable Django templates also allow you to define page blocks to override content on a page by page basis. This process makes a web site's pages more modular because you define top level blocks to establish the overall layout and define content on a page by page basis.

Lets take a look at how the Django built-in {% block %} tag works. Listing 1 illustrates the first lines of a template called base.html with several {% block %} tags.

Listing 1 - Django template with {% block %} tags

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{% block title%}Default title{% endblock%}</title>
    <meta name="description" content="{% block metadescription%}{% endblock %}">
    <meta name="keywords" content="{% block metakeywords%}{% endblock %}">

Notice the syntax {% block <name>%}{% endblock%} in listing 1. Each {% block %} tag has a reference name. The reference name is used by other Django templates to override the content for each block. For example, the {% block title %} tag within the HTML <title> tags defines a web page title. If another template reuses the template in listing 1, it can define its own web page title by overriding the title block. If a block is not overridden on a template, the block receives the default content within the block. For the title block the default content is Default title, for the metadescription and metakeywords blocks the default content is an empty string.

The same mechanism illustrated in listing 1 can be used to define any number of blocks (e.g. content, menu, header, footer). This process allows you to create and test an overall layout to use across all Django templates. The template in listing 1 is not intended to be called directly by a Django view method or url request, but rather be used as a base template for other templates called by Django view methods and url requests. To reuse a Django template you use the Django built-in {% extends %} tag.

The {% extends %} tag uses the syntax {% extends <name> %} to reuse the layout of another template. This means that in order to reuse the layout in listing 1 defined in a file base.html, you use the syntax {% extends "base.html" %}. In addition, if you use the {% extends %} tag it has to be the first definition in Django template, as illustrated in listing 2.

Listing 2 - Django template with {% extends %} and {% block %} tag

{% extends "base.html" %} 
{% block title %}Coffeehouse home page{% endblock %} 

Notice in listing 2 how the first template statement is {% extends "base.html" %}. In addition, notice how listing 2 defines the {% block title %} tag with the content Coffeehouse home page. The block in listing 2 overrides the title block from the base.html template. So where are the HTML <title> tags in listing 2 ? There aren't any and you don't need them. Django automatically reuses the layout from the base.html template and substitutes the blocks content where necessary.

Django templates that reuse other templates tend to have limited layout elements (e.g. HTML tags) and more Django block statements to override content. This is beneficial because as I outlined previously, it lets you establish the overall layout once and define content on a page by page basis.

The re-usability of Django templates can occur multiple times. For example, you can have templates A, B and C, where B requires to reuse A, but C requires to reuse parts of B. The only difference is template C needs to use the {% extends "B" %} tag instead of the {% extends "A"%} tag. But since template B reuses A, template C also has access to the same elements in template A.

When reusing Django templates, it's also possible to access the block content from a parent template. Django exposes the block content from a parent template through the reference block.super. Listing 3 illustrates three templates that show this mechanism for a block containing web page paths or 'breadcrumbs'.

Listing 3 - Django templates use of {{block.super}} with three reusable templates

# base.html template 
<p>{% block breadcrumb %}Home{% endblock %}</p>

# index.html template
{% extends "base.html" %} 
{% block breadcrumb %}Main{% endblock %} 

# detail.html template
{% extends "index.html" %} 
{% block breadcrumb %} {{block.super}} : Detail {% endblock %} 

The base.html template in listing 1 defines the breadcrumb block with a default value of Home. Next, the index.html template reuses the base.html template and overrides the breadcrumb block with a value of Main. Finally, the detail.html template reuses the index.html template and overrides the breadcrumb block value. However, notice the {{block.super}} statement in the final block override. Since {{block.super}} is inside the breadcrumb block, {{block.super}} tells Django to get the content from the parent template block.

Another re-usability functionality supported by Django templates is the inclusion of a Django template inside another Django template. Django supports this functionality through the {% include %} tag.

By default, the {% include %} tag expects the name of a template. For example, {% include "footer.html" %} inserts the contents of the footer.html template in the position of the template where it's declared. The {% include %} tag also makes the underlying template aware of variables. This meas the footer.html template can have variable definitions (e.g.{{year}}) and if the calling template has these variable definitions, the {% include %} tag automatically substitutes these values.

Inclusively, it's possible to provide variables to the {% include %} tag using the with keyword, in case the calling template doesn't have the necessary variable definitions. So for example, the statement {% include "footer.html" with year="2013" %} makes the year variable accesible inside the footer.html template.