Python template engine - Jinja2

Programmers usually want to use variables in their program to create different results dynamically from the same program. e.g. The first program for a newbie is usually

1
2
3
print("Hello World")
print("Hello Python")
print("Hello Kai")

The second program will teach us how to get an input from keyboard and print it out.

1
2
myinput = # get input from keyboard
print("Hello {}".format(myinput))

In a template engine, it does similar but much more powful thing. Usually, we call it a template language since it gives simple program capabilities, such as variables, loop and conditions.

Jinja2 is a template engine in python to combine a model(object values), expressions and statement tags with a template.
To understand what Jinja2 does, there are a few thing we need to understand firstly.

  • Template, a predefined the file or string aligned to Jinja2 syntax or senmantic
  • Model or variable, the variable used to replace the placeholder in your template
  • Expression, a function used to change value of the variable
  • Statement tags, logic control statements such as loop or if condition
  • Commnent, description about a template which will be ignored by the engine when rendering
  • Render, the procedure to replace variable or model in a template while following the statement controls

Initial look

Here is a simple tempalte from Jinja2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %} <- statement to loop through navigation
<li><a href="{{ item.href }}">{{ item.caption }}</a></li> <- use the variable 'item' created by statement
{% endfor %}
</ul>

<h1>My Webpage</h1>
{{ my_name }} <- will be replaced as Kai

{# a comment is ignored #} <- will be ignored
</body>
</html>

Let’s look at how jinja will render this template out when given variables. It’s better than just read the explanations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
navigation = [{href='/', caption='Home'}]
my_name='Kai'
Template(template).render(navigation=navigation, my_name=my_name)
///
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
<li><a href="/">Home</a></li> <- Loop is executed and value is replace
</ul>

<h1>My Webpage</h1>
Kai <- my_name is replaced

</body>
</html>

Delimiters

Delimiters is used by jinja engine to look up variables, expressions, statements and comments in a Template, Jinja runs different renders logic depending on the delimiters.

In a programmming language, the compiler or interpreter runs codes line by line, and understands our commands by keywords.
In Jinja2, it’s those special delimiters that the engine understands intially, and then the engine will try to understand the words between those delimiters.

There are a few kinds of delimiters. The default Jinja delimiters are configured as follows.

1
2
3
4
{{ ... }} for Expressions and variables to print to the template output
{% ... %} for Statements
{# ... #} for Comments not included in the template output
# ... ## for Line Statements
  • The advanced users can choose different delimiters when using Jinja2

Expressions

Math

1
{{ XX + - * / // % ** YY }}

String plus

We can concatenate string

1
{{ 'name' ~ name }}

Comparisons

1
{{ XX == != >= <= < > YY }}

Logic

1
2
3
{{ XX and or YY }}
{{ not XX}}
{{ not (XX and YY) }}

Test

A test expression is used against the is operator, a test expression is a function such as defined(value).
Full list of tests expression

1
2
{{ XX in [YY,ZZ]}}
{{ XX is test expression }}

When using a test expression in jinja template, we can ignore the only value parameter as following

1
{{ XX is defined }}

Some expressions have extra parameters, e.g. divisibleby(value, num)

1
{{ XX is divisibleby(3) }}

Object

We can use python method or attribute or items in a var object

1
{{ XX.method() XX.YY XX[YY] }}

Filter

We can alter a value using filter, or I think mapper is more suitable name, it’s similar as XX.map(mapper1).map(mapper2). Full list of filters

1
{{ XX | filter1 | fitler2 }}

Statements

Control statement

In a template, we can use for, if to control simple logic.

1
2
3
4
5
6
7
8
9
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
</ul>

{% if login %} <- test if login is defined, not empty and not false
<div>Home</div>
{% endif %}

https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-control-structures

Define macros

A macro is used to defined command used piece of codes and used in the template as a function.

1
2
3
4
5
6
7
{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{
value|e }}" size="{{ size }}">
{%- endmacro %}

<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

https://jinja.palletsprojects.com/en/2.11.x/templates/#macros
https://jinja.palletsprojects.com/en/2.11.x/templates/#call

Filters

There are built-in filters in Jinja2, such as upper, it can used by statements to make a block of contents to be uppcase

1
2
3
{% filter upper %}
This text becomes uppercase
{% endfilter %}

Assignment

Some times we can to setup some variables in the template, the assginment statement fits in

1
2
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}

Block Assignments

Similar to assignment, we can capture the context in a block to a variable

1
2
3
4
5
6
{% set navigation %}
my name is kai
{% endset %}

Same as
{% set navigation = 'my name is kai' %}