Экранирование и блоки raw, for, if

Продолжаем изучение модуля Jinja и вначале рассмотрим способы экранирования данных в строках. О чем здесь речь? Давайте представим, что мы бы хотели фрагмент шаблона никак не преобразовывать и выводить так, как он записан. Например, вот такую строку:

from jinja2 import Template
 
data = '''Модуль Jinja вместо
определения {{ name }}
подставляет соответствующее значение'''
 
tm = Template(data)
msg = tm.render(name='Федор')
 
print(msg)

Если выполнить эту программу, то на выходе получим строку:

Модуль Jinja вместо
определения Федор
подставляет соответствующее значение

Как вы понимаете, это не то, что мы бы хотели видеть. Нам нужно оставить эти фигурные скобки так, как они записаны без обработки. Как раз для этого используется специальное определение:

{% raw %} … {% endraw %}

И все записанное внутри этого блока не подвергается преобразованию модулем Jinja. То есть, если в строке data прописать этот блок:

data = '''{% raw %}Модуль Jinja вместо
определения {{ name }}
подставляет соответствующее значение{% endraw %}'''

То на выходе будет именно этот текст без каких-либо подстановок. Вот так работает блок raw.

Экранирование символов

Далее, при работе с текстовыми HTML-шаблонами часто возникает необходимость экранирования некоторых символов, которые браузерами воспринимаются как определение тегов. Например, вот такая программа:

link = '''В HTML-документе ссылки определяются так: 
<a href="#">Ссылка</a>'''
 
tm = Template("{{ link }}")
msg = tm.render(link = link)
 
print(msg)

На выходе дает строку:

В HTML-документе ссылки определяются так:

<a href="#">Ссылка</a>

И если, затем ее вывести как фрагмент HTML-документа, то мы увидим в окне браузера:

А хотелось бы увидеть именно определение тега <a> вместо самой ссылки. Для этого внутри фигурных скобок можно прописывать различные флаги и один из них

e – escape (экранирование)

Применить его можно так:

tm = Template("{{ link | e }}")

Теперь, на выходе имеем:

В HTML-документе ссылки определяются так:

&lt;a href=&#34;#&#34;&gt;Ссылка&lt;/a&gt;

Смотрите, здесь вместо угловых скобок и кавычек прописаны специальные символы, которые браузером будут восприниматься как текстовый фрагмент и отображаться в виде:

И это именно то, что мы бы хотели получить в данном случае. Подобное преобразование довольно часто требуется выполнять при работе с шаблонами страниц сайтов, поэтому в модуле Jinja существует специальный класс escape, который на выходе выдает строку с экранированными символами:

from jinja2 import Template, escape
 
link = '''В HTML-документе ссылки определяются так: 
<a href="#">Ссылка</a>'''
 
print(escape(link))

Мы здесь уже не используем функционал шаблона, а просто преобразуем строку. Поэтому, для целей простого экранирования, лучше использовать этот метод, т.к. он работает быстрее, нежели через шаблон. Вот так работает экранирование в модуле Jinja.

Выражение for

Следующий вид блока, который мы рассмотрим – это

{% for <выражение> -%}
    <повторяемый фрагмент>
{% endfor %}

Он позволяет формировать список на основе любого итерируемого объекта, например, упорядоченного списка. В качестве примера я приведу такую программу:

cities = [{'id': 1, 'city': 'Москва'},
          {'id': 5, 'city': 'Тверь'},
          {'id': 7, 'city': 'Минск'},
          {'id': 8, 'city': 'Смоленск'},
          {'id': 11, 'city': 'Калуга'}]
 
link = '''<select name="cities">
{% for c in cities %}
    <option value="{{c['id']}}">{{c['city']}}</option>
{% endfor %}
</select>'''
 
tm = Template(link)
msg = tm.render(cities = cities)
 
print(msg)

У нас здесь имеется список из городов и их id (например, в соответствии с rowid БД SQLite) и, затем, формируется тег select для HTML-документа. Внутри размещаются теги option по указанному формату для каждого города. В результате выполнения такого шаблона, получим следующее:

<select name="cities">

    <option value="1">Москва</option>

    <option value="5">Тверь</option>

Мы здесь видим лишний перенос строки между тегами. Это из-за того, что после блока for стоит перенос строки. Убрать его можно двумя способами. Или записать вот в таком виде:

link = '''<select name="cities">
{% for c in cities %}<option value="{{c['id']}}">{{c['city']}}</option>{% endfor %}
</select>'''

Или, поставить знак минус перед закрывающей скобкой:

link = '''<select name="cities">
{% for c in cities -%}
    <option value="{{c['id']}}">{{c['city']}}</option>
{% endfor -%}
</select>'''

Этот минус указывает удалять все переносы и пробелы после блока for и после endfor. Его можно ставить и вначале, тогда будут удалены пробелы и переносы справа от скобки.

Выражение if

Последний тип блока, который мы рассмотрим на этом занятии – это блок для проверки условий. В самом простом варианте он записывается в виде:

{% if  <условие> %}
    <фрагмент при истинности условия>
{% endif %}

И в нашем примере его можно использовать так:

link = '''<select name="cities">
{% for c in cities -%}
{% if c.id > 6 -%}
    <option value="{{c['id']}}">{{c['city']}}</option>
{% endif -%}
{% endfor -%}
</select>'''

Мы здесь добавили проверку, что добавлять следует только те города, у которых id больше 6.

Соответственно, здесь можно добавлять определение else:

link = '''<select name="cities">
{% for c in cities -%}
{% if c.id > 6 -%}
    <option value="{{c['id']}}">{{c['city']}}</option>
{%else -%}
    {{c['city']}}
{% endif -%}
{% endfor -%}
</select>'''

И elif:

link = '''<select name="cities">
{% for c in cities -%}
{% if c.id > 6 -%}
    <option value="{{c['id']}}">{{c['city']}}</option>
{%elif c.city == "Москва" -%}
    <option>{{c['city']}}</option>
{%else -%}
    {{c['city']}}
{% endif -%}
{% endfor -%}
</select>'''

То есть, этот блок используется ровно так как и оператор if  в Python.