Create reusable Jinja templates

Templates tend to have common sections that are equally used across multiple instances. For example, the header and footer sections on all templates rarely changes, whether a project has five or one hundred templates. Other template sections like menus and advertisements, also fall into this category of content that's constant across multiple templates. All of this can lead to repetition over multiple templates, which can be avoided by creating reusable templates.

With reusable Jinja 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 project's templates because a single template update takes effect on all templates.

Reusable Jinja templates also allow you to define page blocks to override content on a page by page basis. This process makes a project's templates more modular because you define top level blocks to establish the overall layout and define content on a page by page basis.

Lets take the first step toward building reusable Jinja templates by exploring Jinja's built-in {% block %} tag. Listing 4-5 illustrates the first lines of a template called base.html with several {% block %} tags.

Listing 4-5. Jinja template with {% block %} tags

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

Notice the syntax {% block <name>%}{% endblock <name>%} in listing 4-5. Each {% block %} tag has a reference name. The reference name is used by other Jinja 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 4-5, 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 4-5 can be used to define any number of blocks (e.g. content, menu, header, footer). It's worth mentioning the <name> argument of {% endblock <name> %} is optional and it's valid to just use {% endblock %} to close a block statement, however, the former technique makes it clearer where a block statement ends which is specially helpful when a template has multiple blocks.

Although it's possible to call the template in listing 4-5 directly by a Django view method or url request, the purpose of this kind of template is to use it as a base template for other templates. To reuse a Jinja template you use the Jinja 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 4-5 defined in a file base.html, you use the syntax {% extends "base.html" %}, as illustrated in listing 4-6.

Listing 4-6. Jinja template with {% extends %} and {% block %} tag

{% if user %}{% extends "base.html" %}{% else %}{% extends "signup_base.html" %}{% endif %}
{% block title %}Coffeehouse home page{% endblock %} 

Look how listing 4-6 uses the {% extends "base.html" %} wrapped around the {% if user %} statement. If the user variable is defined, Jinja extends the base.html template, otherwise it extends the signup_base.html template. This conditional syntax is not possible in Django templates.

In addition, notice how listing 4-6 defines the {% block title %} tag with the content Coffeehouse home page. The block in listing 4-6 overrides the title block from the base.html template. So where are the HTML <title> tags in listing 4-6 ? There aren't any and you don't need them. Jinja automatically reuses the layout from either the base.html or signup_base.html templates and substitutes the blocks content where necessary.

Jinja templates that reuse other templates tend to have limited layout elements (e.g. HTML tags) and more Jinja 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 Jinja 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 Jinja templates, it's also possible to access the block content from a parent template. Jinja exposes the block content from a parent template through the super() method. Listing 4-7 illustrates three templates that show this mechanism for a block containing web page paths or 'breadcrumbs'.

Listing 4-7. Jinja templates use of 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 %} {{super()}} : Detail {% endblock %} 

The base.html template in listing 4-7 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 {{super()}} statement in the final block override. Since {{super()}} is inside the breadcrumb block, {{super()}} tells Jinja to get the content from the parent template block.

Another re-usability functionality supported by Jinja templates is the inclusion of a Jinja template inside another Jinja template. Jinja 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.

In addition, it's possible to provide a list of templates as a fall-back mechanism. For example, {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} tells Jinja to first attempt to locate the special_sidebar.html template and if it isn't found to attempt to locate the sidebar.html template, if neither template is found the last argument ignore missing tells Jinja to render nothing. Note the ignore missing argument can also be used in individual statements (e.g. {% include "footer.html" ignore missing %}, as well as lists). In addition, if the ignore missing statement is not used and Jinja can't find a matching template declared in {% include %} Jinja raises an exception.

The {% macro %} tag allows the definition of reusable content snippets across templates. For example, if you need to incorporate elaborate markup to display elements that have common characteristics, you can define the elaborate markup once in a {% macro %} statement and then re-use this {% macro %} to output the markup customized to each element instance.

Macros are helpful because if you decide to change the markup, you only need to change it in a single location and the changes propagate to other locations. Listing 4-8 illustrates the definition of a {% macro %} statement and its usage in templates.

Listing 4-8. Jinja {% macro %} definition and use of {% import %}

# base.html template 
{% macro coffeestore(name, id='', address='', city='San Diego', state='CA', email=None) -%}
    <a id="{{id}}"></a>
    <h4>{{name}}</h4>
    <p>{{address}} {{city}},{{state}}</p>
    {% if email %}<p><a href='mailto:{{email}}'>{{email}}</a></p>{% endif %}
{%- endmacro %}

# index.html template calls inherited macro directly
{% extends "base.html" %} 
{{coffeestore('Downtown',1,'Horton Plaza','San Diego','CA','downtown@coffeehouse.com')}}

# detail.html template with no extends, uses {% import %} to access macro in base.html
{% import 'base.html' as base %}
{{base.coffeestore('Downtown',1,'Horton Plaza','San Diego','CA','downtown@coffeehouse.com')}}

# otherdetail.html template with no extends, uses {% from import %} to access macro in base.html
{% from 'base.html' import coffeestore as mycoffeestoremacro %}
{{mycoffeestoremacro('Downtown',1,'Horton Plaza','San Diego','CA','downtown@coffeehouse.com')}}

The first thing that's done in listing 4-8 is the {% macro %} definition declared in the base.html template. Notice that after the {% macro snippet, there's what appears to be a regular method named coffeestore, which corresponds to the name of the macro with six input arguments, five of which have default values. Next, inside the {% macro %} and {% endmacro %} statements you can see some elaborate HTML markup that makes use of the standard {{ }} syntax to output whatever variable values are passed on a given instance of the macro.

Since the {% macro %} in listing 4-8 is defined inside the base.html template, any other template that uses the base.html template can access the macro and call the macro with an instance (e.g. {{coffeestore('Downtown',1,'Horton Plaza','San Diego','CA','downtown@coffeehouse.com')}} -- hard-coded values for simplicity) for Jinja to render the HTML markup customized with the instance values.

If you want to access a {% macro %} in other templates you have three alternatives which are also presented in listing 4-8. If a template extends another template (e.g. {% extends "base.html" %}) then by default it will also gain access to the parent's template {% macro %} definitions. It's also possible to access another template's {% macro %} definitions with the {% import %} statement. For example, the statement {% import 'base.html' as base %} imports the base.html definitions into another template with the base namespace, in which case to invoke the {% macro %} called coffeestore you would use the {{base.coffeestore(...}} syntax. Finally, it's also possible to selectively import a {% macro %} definition with the {% from import %} statement. For example, the statement {% from 'base.html' import coffeestore as mycoffeestoremacro %} imports the coffeestore definition from the base.html template and places it under the mycoffeestoremacro name, in which case you would use the {{mycoffeehousemacro(...}} syntax to invoke the {% macro %}.

The {% call %} tag is another option that used in conjunction with the {% macro %} tags favors the re-usability of macros themselves. The first usage scenario of the {% call %} tag is to invoke a {% macro %} that requires a placeholder for content that's defined until the invocation of the macro. Listing 4-9 illustrates this basic scenario of the {% call %} tag along with a {% macro %}.

Listing 4-9. Jinja {% call %} and {% macro %} use

# macro definition
{% macro contentlist(adcolumn_width=3,contentcolumn_width=6) -%}
   <div class="col-md-{{adcolumn_width}}">
    Sidebar ads
   </div>
   <div class="col-md-{{contentcolumn_width}}">
      {{ caller() }}
   </div>
   <div class="col-md-{{adcolumn_width}}">
    Sidebar ads
   </div>
{%- endmacro %}

# macro call/invocation
{% call contentlist() %} 
  <ul>
    <li>This is my list</li> 
  </ul>
{% endcall %}

# rendering
<div class="col-md-3">
    Sidebar ads
</div>
<div class="col-md-6">
  <ul>
    <li>This is my list</li> 
  </ul>
</div>
<div class="col-md-3">
    Sidebar ads
</div>

In listing 4-9 we first define a {% macro %} with a similar structure to that of listing 3-8, however, notice inside the {% macro %} the {{ caller() }} statement. The caller() method inside {% macro %} serves as placeholder to be substituted by the calling entity.

Next, in listing 4-9 you can see the {% call %} statement is declared with the macro call -- in this case contentlist() -- and the body of the {% call %} statement contains an HTML list. When Jinja executes the {% call %} statement, the {% call %} contents are placed in the location of the {% macro %} {{caller()}} declaration.

A more advanced scenario of the {% call %} tag with a {% macro %} is for the caller() statement to use references, a process that's more natural to data that's recursive in nature (i.e. a macro over a macro). Listing 4-10 illustrates this recursive scenario of the {% call %} tag along with a {% macro %}.

Listing 4-10 Jinja {% call %} and {% macro %} recursive calls

# macro definition
{% macro contentlist(itemlist,adcolumn_width=3,contentcolumn_width=6) -%}
   <div class="col-md-{{adcolumn_width}}">
    Sidebar ads
   </div>
   <div class="col-md-{{contentcolumn_width}}">
     {% for item in itemlist %}
      {{ caller(item) }}
     {% endfor %}
   </div>
   <div class="col-md-{{adcolumn_width}}">
    Sidebar ads
   </div>
{%- endmacro %}

# variable definition
{% set coffeestores=[{'id':0,'name':'Corporate','address':'624 Broadway','city':'San Diego','state':'CA',
'email':'corporate@coffeehouse.com'},{'id':1,'name':'Downtown','address':'Horton Plaza','city':'San Diego',
'state':'CA','email':'downtown@coffeehouse.com'},{'id':2,'name':'Uptown','address':'1240 University Ave',
'city':'San Diego','state':'CA','email':'uptown@coffeehouse.com'},{'id':3,'name':'Midtown',
'address':'784 W Washington St','city':'San Diego','state':'CA','email':'midtown@coffeehouse.com'}] %}

# macro call/invocation
{% call(item) contentlist(coffeestores) %} 
    <a id="{{item.id}}"></a>
    <h4>{{item.name}}</h4>
    <p>{{item.address}} {{item.city}},{{item.state}}</p>
    {% if item.email %}<p><a href='mailto:{{item.email}}'>{{item.email}}</a></p>{% endif %}
{% endcall %}

# rendering
<div class="col-md-3">
    Sidebar ads
</div>
<div class="col-md-6">       
    <a id="0"></a>
    <h4>Corporate</h4>
    <p>624 Broadway San Diego,CA</p>
    <p><a href="mailto:corporate@coffeehouse.com">corporate@coffeehouse.com</a></p>
       
    <a id="1"></a>
    <h4>Downtown</h4>
    <p>Horton Plaza San Diego,CA</p>
    <p><a href="mailto:downtown@coffeehouse.com">downtown@coffeehouse.com</a></p>
       
    <a id="2"></a>
    <h4>Uptown</h4>
    <p>1240 University Ave San Diego,CA</p>
    <p><a href="mailto:uptown@coffeehouse.com">uptown@coffeehouse.com</a></p>
       
    <a id="3"></a>
    <h4>Midtown</h4>
    <p>784 W Washington St San Diego,CA</p>
    <p><a href="mailto:midtown@coffeehouse.com">midtown@coffeehouse.com</a></p>
</div>
<div class="col-md-3">
    Sidebar ads
</div>

As you can see in listing 4-10, the {% macro %} definition now has an argument called itemlist on which it creates an iteration and for each item it invokes {{caller(item)}}. Also notice in listing 4-10 the {% call %} statement is now {% call(item) contentlist(coffeestores) %}, where item represents the callback item sent from the macro and contentlist(coffeestores) is the actual call to the macro named contentlist along with its input coffeestores that's a list of dictionaries. When Jinja executes the {% call %} statement, the {% call %} contents are run recursively over each item resulting in the output presented at the bottom of listing 4-10.

Tip The built-in {% set %} statement -- described in the Jinja built-in filters section -- provides simpler re-use functionality for static content blocks compared to {% macro %} statements that use variables. (e.g. {% set advertisement %}<div class='banner'><img src=...></div>{% endset %} creates the advertisement variable that can output the contents between {% set %} and {% endset %} anywhere in a template).