Man, Do I Like Templates!

In this article by Italo Maia, author of the book Building Web Applications with Flask, we will discuss what Jinja2 is, and how Flask uses Jinja2 to implement the View layer and awe you. Be prepared!

(For more resources related to this topic, see here.)

What is Jinja2 and how is it coupled with Flask?

Jinja2 is a library found at http://jinja.pocoo.org/; you can use it to produce formatted text with bundled logic. Unlike the Python format function, which only allows you to replace markup with variable content, you can have a control structure, such as a for loop, inside a template string and use Jinja2 to parse it. Let's consider this example:

from jinja2 import Template
x = """
<p>Uncle Scrooge nephews</p>
<ul>
{% for i in my_list %}
<li>{{ i }}</li>
{% endfor %}
</ul>
"""
template = Template(x)
# output is an unicode string
print template.render(my_list=['Huey', 'Dewey', 'Louie'])

In the preceding code, we have a very simple example where we create a template string with a for loop control structure ("for tag", for short) that iterates over a list variable called my_list and prints the element inside a "li HTML tag" using curly braces {{ }} notation.

Notice that you could call render in the template instance as many times as needed with different key-value arguments, also called the template context. A context variable may have any valid Python variable name—that is, anything in the format given by the regular expression [a-zA-Z_][a-zA-Z0-9_]*.

For a full overview on regular expressions (Regex for short) with Python, visit https://docs.python.org/2/library/re.html. Also, take a look at this nice online tool for Regex testing http://pythex.org/.

A more elaborate example would make use of an environment class instance, which is a central, configurable, extensible class that may be used to load templates in a more organized way.

Do you follow where we are going here? This is the basic principle behind Jinja2 and Flask: it prepares an environment for you, with a few responsive defaults, and gets your wheels in motion.

What can you do with Jinja2?

Jinja2 is pretty slick. You can use it with template files or strings; you can use it to create formatted text, such as HTML, XML, Markdown, and e-mail content; you can put together templates, reuse templates, and extend templates; you can even use extensions with it. The possibilities are countless, and combined with nice debugging features, auto-escaping, and full unicode support.

Auto-escaping is a Jinja2 configuration where everything you print in a template is interpreted as plain text, if not explicitly requested otherwise. Imagine a variable x has its value set to <b>b</b>. If auto-escaping is enabled, {{ x }} in a template would print the string as given. If auto-escaping is off, which is the Jinja2 default (Flask's default is on), the resulting text would be b.

Let's understand a few concepts before covering how Jinja2 allows us to do our coding.

First, we have the previously mentioned curly braces. Double curly braces are a delimiter that allows you to evaluate a variable or function from the provided context and print it into the template:

from jinja2 import Template
# create the template
t = Template("{{ variable }}")
# – Built-in Types –
t.render(variable='hello you')
>> u"hello you"
t.render(variable=100)
>> u"100"
# you can evaluate custom classes instances
class A(object):
def __str__(self):
   return "__str__"
def __unicode__(self):
   return u"__unicode__"
def __repr__(self):
   return u"__repr__"
# – Custom Objects Evaluation –
# __unicode__ has the highest precedence in evaluation
# followed by __str__ and __repr__
t.render(variable=A())
>> u"__unicode__"

In the preceding example, we see how to use curly braces to evaluate variables in your template. First, we evaluate a string and then an integer. Both result in a unicode string. If we evaluate a class of our own, we must make sure there is a __unicode__ method defined, as it is called during the evaluation. If a __unicode__ method is not defined, the evaluation falls back to __str__ and __repr__, sequentially. This is easy. Furthermore, what if we want to evaluate a function? Well, just call it:

from jinja2 import Template
# create the template
t = Template("{{ fnc() }}")
t.render(fnc=lambda: 10)
>> u"10"
# evaluating a function with argument
t = Template("{{ fnc(x) }}")
t.render(fnc=lambda v: v, x='20')
>> u"20"
t = Template("{{ fnc(v=30) }}")
t.render(fnc=lambda v: v)
>> u"30"

To output the result of a function in a template, just call the function as any regular Python function. The function return value will be evaluated normally. If you're familiar with Django, you might notice a slight difference here. In Django, you do not need the parentheses to call a function, or even pass arguments to it. In Flask, the parentheses are always needed if you want the function return evaluated.

The following two examples show the difference between Jinja2 and Django function call in a template:

{# flask syntax #}
{{ some_function() }}
 
{# django syntax #}
{{ some_function }}

You can also evaluate Python math operations. Take a look:

from jinja2 import Template
# no context provided / needed
Template("{{ 3 + 3 }}").render()
>> u"6"
Template("{{ 3 - 3 }}").render()
>> u"0"
Template("{{ 3 * 3 }}").render()
>> u"9"
Template("{{ 3 / 3 }}").render()
>> u"1"

Other math operators will also work. You may use the curly braces delimiter to access and evaluate lists and dictionaries:

from jinja2 import Template
Template("{{ my_list[0] }}").render(my_list=[1, 2, 3])
>> u'1'
Template("{{ my_list['foo'] }}").render(my_list={'foo': 'bar'})
>> u'bar'
# and here's some magic
Template("{{ my_list.foo }}").render(my_list={'foo': 'bar'})
>> u'bar'

To access a list or dictionary value, just use normal plain Python notation. With dictionaries, you can also access a key value using variable access notation, which is pretty neat.

Besides the curly braces delimiter, Jinja2 also has the curly braces/percentage delimiter, which uses the notation {% stmt %} and is used to execute statements, which may be a control statement or not. Its usage depends on the statement, where control statements have the following notation:

{% stmt %}
{% endstmt %}

The first tag has the statement name, while the second is the closing tag, which has the name of the statement appended with end in the beginning. You must be aware that a non-control statement may not have a closing tag. Let's look at some examples:

{% block content %}
{% for i in items %}
{{ i }} - {{ i.price }}
{% endfor %}
{% endblock %}

The preceding example is a little more complex than what we have been seeing. It uses a control statement for loop inside a block statement (you can have a statement inside another), which is not a control statement, as it does not control execution flow in the template. Inside the for loop you see that the i variable is being printed together with the associated price (defined elsewhere).

A last delimiter you should know is {# comments go here #}. It is a multi-line delimiter used to declare comments. Let's see two examples that have the same result:

{# first example #}
{#
second example
#}

Both comment delimiters hide the content between {# and #}. As can been seen, this delimiter works for one-line comments and multi-line comments, what makes it very convenient.

Control structures

We have a nice set of built-in control structures defined by default in Jinja2. Let's begin our studies on it with the if statement.

{% if true %}Too easy{% endif %}
{% if true == true == True %}True and true are the same{% endif %}
{% if false == false == False %}False and false also are the same{% endif %}
{% if none == none == None %}There's also a lowercase None{% endif %}
{% if 1 >= 1 %}Compare objects like in plain python{% endif %}
{% if 1 == 2 %}This won't be printed{% else %}This will{% endif %}
{% if "apples" != "oranges" %}All comparison operators work = ]{% endif %}
{% if something %}elif is also supported{% elif something_else %}^_^{% endif %}

The if control statement is beautiful! It behaves just like a python if statement. As seen in the preceding code, you can use it to compare objects in a very easy fashion. "else" and "elif" are also fully supported.

You may also have noticed that true and false, non-capitalized, were used together with plain Python Booleans, True and False. As a design decision to avoid confusion, all Jinja2 templates have a lowercase alias for True, False, and None. By the way, lowercase syntax is the preferred way to go.

If needed, and you should avoid this scenario, you may group comparisons together in order to change precedence evaluation. See the following example:

{% if 5 < 10 < 15 %}true{%else%}false{% endif %}
{% if (5 < 10) < 15 %}true{%else%}false{% endif %}
{% if 5 < (10 < 15) %}true{%else%}false{% endif %}

The expected output for the preceding example is true, true, and false. The first two lines are pretty straightforward. In the third line, first, (10<15) is evaluated to True, which is a subclass of int, where True == 1. Then 5 < True is evaluated, which is certainly false.

The for statement is pretty important. One can hardly think of a serious Web application that does not have to show a list of some kind at some point. The for statement can iterate over any iterable instance and has a very simple, Python-like syntax:

{% for item in my_list %}
{{ item }}{# print evaluate item #}
{% endfor %}
{# or #}
{% for key, value in my_dictionary.items() %}
{{ key }}: {{ value }}
{% endfor %}

In the first statement, we have the opening tag indicating that we will iterate over my_list items and each item will be referenced by the name item. The name item will be available inside the for loop context only.

In the second statement, we have an iteration over the key value tuples that form my_dictionary, which should be a dictionary (if the variable name wasn't suggestive enough). Pretty simple, right? The for loop also has a few tricks in store for you.

When building HTML lists, it's a common requirement to mark each list item in alternating colors in order to improve readability or mark the first or/and last item with some special markup. Those behaviors can be achieved in a Jinja2 for-loop through access to a loop variable available inside the block context. Let's see some examples:

{% for i in ['a', 'b', 'c', 'd'] %}
{% if loop.first %}This is the first iteration{% endif %}
{% if loop.last %}This is the last iteration{% endif %}
{{ loop.cycle('red', 'blue') }}{# print red or blue alternating #}
{{ loop.index }} - {{ loop.index0 }} {# 1 indexed index – 0 indexed index #}
{# reverse 1 indexed index – reverse 0 indexed index #}
{{ loop.revindex }} - {{ loop.revindex0 }}
{% endfor %}

The for loop statement, as in Python, also allow the use of else, but with a slightly different meaning. In Python, when you use else with for, the else block is only executed if it was not reached through a break command like this:

for i in [1, 2, 3]:
pass
else:
print "this will be printed"
for i in [1, 2, 3]:
if i == 3:
   break
else:
print "this will never not be printed"

As seen in the preceding code snippet, the else block will only be executed in a for loop if the execution was never broken by a break command. With Jinja2, the else block is executed when the for iterable is empty. For example:

{% for i in [] %}
{{ i }}
{% else %}I'll be printed{% endfor %}
{% for i in ['a'] %}
{{ i }}
{% else %}I won't{% endfor %}

As we are talking about loops and breaks, there are two important things to know: the Jinja2 for loop does not support break or continue. Instead, to achieve the expected behavior, you should use loop filtering as follows:

{% for i in [1, 2, 3, 4, 5] if i > 2 %}
value: {{ i }}; loop.index: {{ loop.index }}
{%- endfor %}

In the first tag you see a normal for loop together with an if condition. You should consider that condition as a real list filter, as the index itself is only counted per iteration. Run the preceding example and the output will be the following:

value:3; index: 1
value:4; index: 2
value:5; index: 3

Look at the last observation in the preceding example—in the second tag, do you see the dash in {%-? It tells the renderer that there should be no empty new lines before the tag at each iteration. Try our previous example without the dash and compare the results to see what changes.

We'll now look at three very important statements used to build templates from different files: block, extends, and include.

block and extends always work together. The first is used to define "overwritable" blocks in a template, while the second defines a parent template that has blocks, for the current template. Let's see an example:

# coding:utf-8
with open('parent.txt', 'w') as file:
   file.write("""
{% block template %}parent.txt{% endblock %}
===========
I am a powerful psychic and will tell you your past
 
{#- "past" is the block identifier #}
{% block past %}
You had pimples by the age of 12.
{%- endblock %}
 
Tremble before my power!!!""".strip())
 
with open('child.txt', 'w') as file:
   file.write("""
{% extends "parent.txt" %}
 
{# overwriting the block called template from parent.txt #}
{% block template %}child.txt{% endblock %}
 
{#- overwriting the block called past from parent.txt #}
{% block past %}
You've bought an ebook recently.
{%- endblock %}""".strip())
with open('other.txt', 'w') as file:
   file.write("""
{% extends "child.txt" %}
{% block template %}other.txt{% endblock %}""".strip())
 
from jinja2 import Environment, FileSystemLoader
 
env = Environment()
# tell the environment how to load templates
env.loader = FileSystemLoader('.')
# look up our template
tmpl = env.get_template('parent.txt')
# render it to default output
print tmpl.render()
print ""
# loads child.html and its parent
tmpl = env.get_template('child.txt')
print tmpl.render()
# loads other.html and its parent
env.get_template('other.txt').render()

Do you see the inheritance happening, between child.txt and parent.txt? parent.txt is a simple template with two block statements, called template and past. When you render parent.txt directly, its blocks are printed "as is", because they were not overwritten. In child.txt, we extend the parent.txt template and overwrite all its blocks. By doing that, we can have different information in specific parts of a template without having to rewrite the whole thing.

With other.txt, for example, we extend the child.txt template and overwrite only the block-named template. You can overwrite blocks from a direct parent template or from any of its parents.

If you were defining an index.txt page, you could have default blocks in it that would be overwritten when needed, saving lots of typing.

Explaining the last example, Python-wise, is pretty simple. First, we create a Jinja2 environment (we talked about this earlier) and tell it how to load our templates, then we load the desired template directly. We do not have to bother telling the environment how to find parent templates, nor do we need to preload them.

The include statement is probably the easiest statement so far. It allows you to render a template inside another in a very easy fashion. Let's look at an example:

with open('base.txt', 'w') as file:
file.write("""
{{ myvar }}
You wanna hear a dirty joke?
{% include 'joke.txt' %}
""".strip())
with open('joke.txt', 'w') as file:
file.write("""
A boy fell in a mud puddle. {{ myvar }}
""".strip())
 
from jinja2 import Environment, FileSystemLoader
 
env = Environment()
# tell the environment how to load templates
env.loader = FileSystemLoader('.')
print env.get_template('base.txt').render(myvar='Ha ha!')

In the preceding example, we render the joke.txt template inside base.txt. As joke.txt is rendered inside base.txt, it also has full access to the base.txt context, so myvar is printed normally.

Finally, we have the set statement. It allows you to define variables for inside the template context. Its use is pretty simple:

{% set x = 10 %}
{{ x }}
{% set x, y, z = 10, 5+5, "home" %}
{{ x }} - {{ y }} - {{ z }}

In the preceding example, if x was given by a complex calculation or a database query, it would make much more sense to have it cached in a variable, if it is to be reused across the template. As seen in the example, you can also assign a value to multiple variables at once.

Macros

Macros are the closest to coding you'll get inside Jinja2 templates. The macro definition and usage are similar to plain Python functions, so it is pretty easy. Let's try an example:

with open('formfield.html', 'w') as file:
file.write('''
{% macro input(name, value='', label='') %}
{% if label %}
<label for='{{ name }}'>{{ label }}</label>
{% endif %}
<input id='{{ name }}' name='{{ name }}' value='{{ value }}'></input>
{% endmacro %}'''.strip())
with open('index.html', 'w') as file:
file.write('''
{% from 'formfield.html' import input %}
<form method='get' action='.'>
{{ input('name', label='Name:') }}
<input type='submit' value='Send'></input>
</form>
'''.strip())
 
from jinja2 import Environment, FileSystemLoader
 
env = Environment()
env.loader = FileSystemLoader('.')
print env.get_template('index.html').render()

In the preceding example, we create a macro that accepts a name argument and two optional arguments: value and label. Inside the macro block, we define what should be output. Notice we can use other statements inside a macro, just like a template. In index.html we import the input macro from inside formfield.html, as if formfield was a module and input was a Python function using the import statement. If needed, we could even rename our input macro like this:

{% from 'formfield.html' import input as field_input %}

You can also import formfield as a module and use it as follows:

{% import 'formfield.html' as formfield %}

When using macros, there is a special case where you want to allow any named argument to be passed into the macro, as you would in a Python function (for example, **kwargs). With Jinja2 macros, these values are, by default, available in a kwargs dictionary that does not need to be explicitly defined in the macro signature. For example:

# coding:utf-8
with open('formfield.html', 'w') as file:
   file.write('''
{% macro input(name) -%}
<input id='{{ name }}' name='{{ name }}' {% for k,v in kwargs.items() -%}{{ k }}='{{ v }}' {% endfor %}></input>
{%- endmacro %}
'''.strip())with open('index.html', 'w') as file:
   file.write('''
{% from 'formfield.html' import input %}
{# use method='post' whenever sending sensitive data over HTTP #}
<form method='post' action='.'>
{{ input('name', type='text') }}
{{ input('passwd', type='password') }}
<input type='submit' value='Send'></input>
</form>
'''.strip())
 
from jinja2 import Environment, FileSystemLoader
 
env = Environment()
env.loader = FileSystemLoader('.')
print env.get_template('index.html').render()

As you can see, kwargs is available even though you did not define a kwargs argument in the macro signature.

Macros have a few clear advantages over plain templates, that you notice with the include statement:

  • You do not have to worry about variable names in the template using macros
  • You can define the exact required context for a macro block through the macro signature
  • You can define a macro library inside a template and import only what is needed

Commonly used macros in a Web application include a macro to render pagination, another to render fields, and another to render forms. You could have others, but these are pretty common use cases.

Regarding our previous example, it is good practice to use HTTPS (also known as, Secure HTTP) to send sensitive information, such as passwords, over the Internet. Be careful about that!

Extensions

Extensions are the way Jinja2 allows you to extend its vocabulary. Extensions are not enabled by default, so you can enable an extension only when and if you need, and start using it without much trouble:

env = Environment(extensions=['jinja2.ext.do',   'jinja2.ext.with_'])

In the preceding code, we have an example where you create an environment with two extensions enabled: do and with. Those are the extensions we will study in this article.

As the name suggests, the do extension allows you to "do stuff". Inside a do tag, you're allowed to execute Python expressions with full access to the template context. Flask-Empty, a popular flask boilerplate available at https://github.com/italomaia/flask-empty uses the do extension to update a dictionary in one of its macros, for example. Let's see how we could do the same:

{% set x = {1:'home', '2':'boat'} %}
{% do x.update({3: 'bar'}) %}
{%- for key,value in x.items() %}
{{ key }} - {{ value }}
{%- endfor %}

In the preceding example, we create the x variable with a dictionary, then we update it with {3: 'bar'}. You don't usually need to use the do extension but, when you do, a lot of coding is saved.

The with extension is also very simple. You use it whenever you need to create block scoped variables. Imagine you have a value you need cached in a variable for a brief moment; this would be a good use case. Let's see an example:

{% with age = user.get_age() %}
My age: {{ age }}
{% endwith %}
My age: {{ age }}{# no value here #}

As seen in the example, age exists only inside the with block. Also, variables set inside a with block will only exist inside it. For example:

{% with %}
{% set count = query.count() %}
Current Stock: {{ count }}
Diff: {{ prev_count - count }}
{% endwith %}
{{ count }} {# empty value #}

Filters

Filters are a marvelous thing about Jinja2! This tool allows you to process a constant or variable before printing it to the template. The goal is to implement the formatting you want, strictly in the template.

To use a filter, just call it using the pipe operator like this:

{% set name = 'junior' %}
{{ name|capitalize }} {# output is Junior #}

Its name is passed to the capitalize filter that processes it and returns the capitalized value. To inform arguments to the filter, just call it like a function, like this:

{{ ['Adam', 'West']|join(' ') }} {# output is Adam West #}

The join filter will join all values from the passed iterable, putting the provided argument between them.

Jinja2 has an enormous quantity of available filters by default. That means we can't cover them all here, but we can certainly cover a few. capitalize and lower were seen already. Let's look at some further examples:

{# prints default value if input is undefined #}
{{ x|default('no opinion') }}
{# prints default value if input evaluates to false #}
{{ none|default('no opinion', true) }}
{# prints input as it was provided #}
{{ 'some opinion'|default('no opinion') }}
 
{# you can use a filter inside a control statement #}
{# sort by key case-insensitive #}
{% for key in {'A':3, 'b':2, 'C':1}|dictsort %}{{ key }}{% endfor %}
{# sort by key case-sensitive #}
{% for key in {'A':3, 'b':2, 'C':1}|dictsort(true) %}{{ key }}{% endfor %}
{# sort by value #}
{% for key in {'A':3, 'b':2, 'C':1}|dictsort(false, 'value') %}{{ key }}{% endfor %}
{{ [3, 2, 1]|first }} - {{ [3, 2, 1]|last }}
{{ [3, 2, 1]|length }} {# prints input length #}
{# same as in python #}
{{ '%s, =D'|format("I'm John") }}
{{ "He has two daughters"|replace('two', 'three') }}
{# safe prints the input without escaping it first#}
{{ '<input name="stuff" />'|safe }}
{{ "there are five words here"|wordcount }}

Try the preceding example to see exactly what each filter does.

After reading this much about Jinja2, you're probably thinking: "Jinja2 is cool but this is a book about Flask. Show me the Flask stuff!". Ok, ok, I can do that!

Of what we have seen so far, almost everything can be used with Flask with no modifications. As Flask manages the Jinja2 environment for you, you don't have to worry about creating file loaders and stuff like that. One thing you should be aware of, though, is that, because you don't instantiate the Jinja2 environment yourself, you can't really pass to the class constructor, the extensions you want to activate.

To activate an extension, add it to Flask during the application setup as follows:

from flask import Flask
app = Flask(__name__)
app.jinja_env.add_extension('jinja2.ext.do') # or jinja2.ext.with_
if __name__ == '__main__':
app.run()

Messing with the template context

You can use the render_template method to load a template from the templates folder and then render it as a response.

from flask import Flask, render_template
app = Flask(__name__)
 
@app.route("/")
def hello():
   return render_template("index.html")

If you want to add values to the template context, as seen in some of the examples in this article, you would have to add non-positional arguments to render_template:

from flask import Flask, render_template
app = Flask(__name__)
 
@app.route("/")
def hello():
   return render_template("index.html", my_age=28)

In the preceding example, my_age would be available in the index.html context, where {{ my_age }} would be translated to 28. my_age could have virtually any value you want to exhibit, actually.

Now, what if you want all your views to have a specific value in their context, like a version value—some special code or function; how would you do it? Flask offers you the context_processor decorator to accomplish that. You just have to annotate a function that returns a dictionary and you're ready to go. For example:

from flask import Flask, render_response
app = Flask(__name__)
 
@app.context_processor
def luck_processor():
from random import randint
def lucky_number():
   return randint(1, 10)
return dict(lucky_number=lucky_number)
 
@app.route("/")
def hello():
# lucky_number will be available in the index.html context by default
return render_template("index.html")

Summary

In this article, we saw how to render templates using only Jinja2, how control statements look and how to use them, how to write a comment, how to print variables in a template, how to write and use macros, how to load and use extensions, and how to register context processors. I don't know about you, but this article felt like a lot of information! I strongly advise you to run the experiment with the examples. Knowing your way around Jinja2 will save you a lot of headaches.

Resources for Article:


Further resources on this subject: Recommender systems dissected


You've been reading an excerpt of:

Building Web Applications with Flask

Explore Title
comments powered by Disqus