Jinja2: if и for

"Перед прочтением этой статьи советуем ознакомиться с тем, что такое Jinja. Это можно сделать в предыдущей нашей статье.

В предыдущей статье вы узнали как подставлять переменные в шаблон. Мы создали 6 переменных и подставили их в три карточки:

rendered_page = template.render(
    cap1_title="Красная кепка",
    cap1_text="$ 100.00",
    cap2_title="Чёрная кепка",
    cap2_text="$ 120.00",
    cap3_title="Ещё одна чёрная кепка",
    cap3_text="$ 90.00",
)

screenshot

Эта схема совершенно не расширяемая: а что, если товаров в магазине будет 12? Создавать 24 переменные? Нет, вместо этого можно создать список из объектов и пройти его в цикле прямо внутри HTML-шаблона.

В этой статье мы рассмотрим if и for в шаблонах Jinja.

Цикл for

Начнём с вывода 6 карточек одной и той же кепки. У каждой карточки есть свой блок вёрстки. Как его найти можно прочитать здесь.

Мы его нашли, вот он:

...
<div class="col-4">
  <div class="card">
    <img src="https://dvmn.org/filer/canonical/1569333143/329/" class="card-img-top" alt="...">
    <div class="card-body">
      <h5 class="card-title">New York Yankees</h5>
      <p class="card-text">Красная кепка NY Yankees за 20$ снова в продаже!</p>
      <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
    </div>
  </div>
</div>
...

Далее хочется её продублировать 6 раз. Для этого в Jinja есть range(), такой же, как в Python:

...
{% for index in range(6) %}
  <div class="col-4">
    <div class="card">
      <img src="https://dvmn.org/filer/canonical/1569333143/329/" class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">New York Yankees</h5>
        <p class="card-text">Красная кепка NY Yankees за 20$ снова в продаже!</p>
        <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
      </div>
    </div>
  </div>
{% endfor %}
...

Итак, мы просто добавили {% for index in range(6) %} сверху и {% endfor %} снизу. В целом, это тот же for из Python, просто он заключён в значки {% %} и в конце указывается {% endfor %}. После запуска main.py появится index.html с таким результатом:

screenshot

Итерация по списку

6 одинаковых карточек — это не интересно. Карточки можно сделать разными, если названия кепок и их цены брать из списка. Добавим в main.py список из словарей с данными о кепках:

caps = [
    {
        "title": "Красная кепка",
        "price": "$ 100.00",
        "image": "https://dvmn.org/filer/canonical/1569333143/329/"
    },
    ...
]

Полную верию данных можно найти здесь

Теперь вы можете передать в шаблон сразу список словарей:

rendered_page = template.render(caps=caps)

В шаблоне можно написать цикл по списку caps:

{% for cap in caps %}
  <div class="col-4">
    <div class="card">
      <img src="{{cap.image}}" class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">{{cap.title}}</h5>
        <p class="card-text">{{cap.price}}</p>
        <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
      </div>
    </div>
  </div>
{% endfor %}

Обратите внимание: к элементам словаря доступ идёт через точку: {{cap.image}}. В остальном, это почти такой же цикл, как и с range. После запуска main.py получается такой результат:

screenshot

Условия if

Предположим, что у некоторых товаров ещё нет картинки. Что же делать? Без неё, на странице будет уродливая ошибка:

screenshot

В такой ситуации поможет if. Давайте, если картинки нет, заменять её на "стандартную" картинку. Делается это очень просто:

{% if cap.image %}
  <img src="{{cap.image}}" class="card-img-top" alt="...">
{% else %}
  <img src="https://dvmn.org/filer/canonical/1569944855/354/" class="card-img-top" alt="...">
{% endif %}

Условия в Jinja2 работают так же, как и в Python. Если картинка есть, сработает тот блок, что выше, а если нет — тот, что ниже. Получится такой результат:

screenshot"