Сообщить об ошибке.

Циклы for/in в шаблонах jinja2 в Python

Цикл 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 %}