Цикл for/in
в шаблонах jinja2
необходимо располагать внутри блоков {% ... %}
. Например, чтобы отобразить список пользователей, указанный в переменной с именем users
:
<h1>Members</h1> <ul> {% for user in users %} {% фильтр `|e` - экранирует HTML %} <li>{{ user.username|e }}</li> {% endfor %} </ul>
Так как переменные в шаблонах сохраняют свои свойства объектов, можно перебирать контейнеры, такие как словари Python dict
:
<dl> {% for key, value in my_dict.items() %} {% фильтр `|e` - экранирует HTML %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %} </dl>
Обратите внимание, что словари Python по умолчанию не отсортированы, для их сортировки, в шаблон, можно либо передать отсортированный список кортежей, либо collections.OrderedDict()
, либо использовать фильтр dictsort()
.
Внутри блока цикла шаблона for/in
можно получить доступ к специальным переменным:
loop.index
: текущая итерация цикла (начинается с 1);loop.index0
: текущая итерация цикла (начинается с 0);loop.revindex
: количество итераций с конца цикла (начинается с 1);loop.revindex0
: количество итераций с конца цикла (начинается с 0);loop.first
: возвращает True
, если это первая итерация;loop.last
: возвращает True
, если это последняя итерацияloop.length
: количество элементов в последовательности.loop.cycle
: вспомогательная функция для циклического переключения между списком последовательностей. Объяснение как пользоваться ниже;loop.depth
: указывает, насколько глубоко в рекурсивном цикле находится текущий рендеринг. Начинается с уровня 1;loop.depth0
: указывает, насколько глубоко в рекурсивном цикле находится текущий рендеринг. Начинается с уровня 0;loop.previtem
: элемент из предыдущей итерации цикла. Не определено на первой итерации;loop.nextitem
: элемент из следующей итерации цикла. Не определено на последней итерации.loop.changed(*val)
: возвращает True
, если ранее вызывалась с другим значением (или не вызывалась вообще).Внутри цикла шаблона for/in
можно переключаться на каждой итерации между списком строк/переменных, используя специальный помощник loop.cycle
:
{% for row in rows %} <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li> {% endfor %}
В отличие от Python, в шаблонах Jinja2 невозможно прервать цикл оператором break
или пропустить итерацию continue
. НО в циклах шаблонов можно фильтровать последовательность во время итерации, что позволяет пропускать определенные элементы. В следующем примере пропускаются все скрытые пользователи:
{% for user in users if not user.hidden %} <li>{{ user.username|e }}</li> {% endfor %}
Преимущество фильтра, встроенного в цикл, заключается в том, что специальная переменная цикла loop
будет правильно считать итерации, т.е. loop
не будет считать отфильтрованных пользователей.
Если последовательность оказалась пустой или встроенный в цикл шаблона фильтр удалил все элементы из последовательности, то можно визуализировать "пустой" блок {% for ... %}
с помощью блоки {% else %}
:
<ul> {% for user in users %} <li>{{ user.username|e }}</li> {% else %} <li><em>no users found</em></li> {% endfor %} </ul>
Обратите внимание, что в Python блок else
выполняется тогда, когда соответствующий цикл не прерывается оператором break
. Так как циклы шаблонов Jinja2 не могут прерываться оператором break
, то было выбрано несколько иное поведение ключевого слова else
.
Также возможно использовать циклы рекурсивно. Такое поведение полезно, если используются рекурсивные данные, такие как карта сайта или RDFa. Чтобы использовать циклы рекурсивно, нужно добавить рекурсивный модификатор recursive
к определению цикла for/in
и вызвать специальную переменную цикла loop
с новой итерацией.
В следующем примере карта сайта реализуется с рекурсивными циклами:
<ul class="sitemap"> {%- for item in sitemap recursive %} <li><a href="{{ item.href|e }}">{{ item.title }}</a> {%- if item.children -%} <ul class="submenu">{{ loop(item.children) }}</ul> {%- endif %}</li> {%- endfor %} </ul>
Переменная цикла loop
всегда относится к ближайшему внутреннему циклу. Если есть более одного уровня циклов, то можно повторно привязать цикл переменных, написав {% set outer_loop = loop %}
после цикла, который хотим использовать рекурсивно. Затем вызвать его с помощью {{ outer_loop(…) }}
Обратите внимание, что все присвоения переменных в циклах будут очищены в конце итерации и не могут распространяться за область действия этого цикла. Для получения дополнительной информации о том, как это можно обойти, смотрите раздел "Создание и присвоение значений переменным в шаблонах jinja2".
Если необходимо проверить, изменилось ли какое-либо значение с момента последней итерации или изменится ли оно на следующей итерации, то можно использовать специальные переменные цикла loop.previtem
и loop.nextitem
:
{% for value in values %} {% if loop.previtem is defined and value > loop.previtem %} The value just increased! {% endif %} {{ value }} {% if loop.nextitem is defined and loop.nextitem > value %} The value will increase even more! {% endif %} {% endfor %}
Если интересует только то, изменилось ли значение вообще, то использовать специальную переменную цикла loop.changed
еще проще:
{% for entry in entries %} {% if loop.changed(entry.category) %} <h2>{{ entry.category }}</h2> {% endif %} <p>{{ entry.message }}</p> {% endfor %}