Lists and conditional sections are the bread and butter of every template.
{% if %} works like Python's, with elif and else, closed by {% endif %}.
{% for item in items %} loops over any iterable, closed by {% endfor %}. Inside the loop, the special `loop` variable gives:
loop.index(from 1),loop.index0(from 0),loop.revindexloop.first,loop.last,loop.lengthloop.cycle("odd", "even")for alternating values,loop.previtem,loop.nextitem,loop.changed(value)
Loop over dicts with {% for key, value in d.items() %}. Add a filter inline, {% for u in users if u.active %}, so loop.length counts only matching items. An `else` block runs when the loop produced no iterations: perfect for "No results".
There is no break or continue unless you enable the jinja2.ext.loopcontrols extension. Also note that variables set inside a loop do not survive after it; to accumulate, use {% set ns = namespace(total=0) %} and {% set ns.total = ns.total + x %}.
Whitespace control. Tags leave their line breaks in the output, which matters for text formats. Add - inside a delimiter to strip whitespace on that side: {%- if x %} strips before, {{ value -}} after. Or set trim_blocks=True (remove the first newline after a block tag) and lstrip_blocks=True (strip spaces before a block tag) on the Environment, which is the usual choice for YAML and config files.
Example
from jinja2 import Environment
env = Environment(trim_blocks=True, lstrip_blocks=True)
t = env.from_string("""{% set ns = namespace(total=0) %}
{% for u in users if u.active %}
{{ loop.index }}/{{ loop.length }} {{ u.name }}{{ " (first)" if loop.first }}
{% set ns.total = ns.total + u.score %}
{% else %}
No active users.
{% endfor %}
Total: {{ ns.total }}
{% for k, v in limits.items() %}{{ k }}={{ v }}{{ "," if not loop.last }}{% endfor %}""")
print(t.render(users=[{"name": "Ann", "active": True, "score": 3},
{"name": "Bo", "active": False, "score": 9},
{"name": "Cy", "active": True, "score": 4}],
limits={"cpu": 2, "mem": "1Gi"}))
# 1/2 Ann (first)
# 2/2 Cy
# Total: 7
# cpu=2,mem=1GiRemember
- loop.index, loop.first/last and loop.cycle are available in every for loop
- for ... else handles empty lists; for x in xs if cond filters
- Use namespace() to carry values out of a loop
- - markers or trim_blocks/lstrip_blocks control whitespace
Watch out for
- Setting a variable inside a loop and expecting the change to be visible after it.