Продолжаем изучение
модуля Jinja и подробнее
рассмотрим фильтры, которые удобно применять для получения более сложных
представлений. И первый часто применяемый фильтр – это sum для вычисления
суммы определенной коллекции. Например, у нас имеется список автомобилей:
cars = [
{'model': 'Ауди', 'price': 23000},
{'model': 'Шкода', 'price': 17300},
{'model': 'Вольво', 'price': 44300},
{'model': 'Фольксваген', 'price': 21300}
]
Требуется
вывести суммарную цену всех автомобилей. Для этого можно прописать такой
шаблон:
tpl = "Суммарная цена автомобилей {{ cs | sum(attribute='price') }}"
tm = Template(tpl)
msg = tm.render(cs = cars)
print(msg)
Смотрите, мы
здесь сначала указываем коллекцию cs, из которой следует брать
атрибут price и суммировать
его значения по всем автомобилям. В результате применения такого фильтра в
фигурных скобках будет подставлено одно число, равное сумме всех полей price:
Суммарная цена
автомобилей 105900
А вот если
убрать этот фильтр:
tpl = "Суммарная цена автомобилей {{ cs }}"
то увидим
коллекцию автомобилей:
Суммарная цена
автомобилей [{'model': 'Ауди', 'price': 23000}, {'model': 'Шкода', 'price':
17300}, {'model': 'Вольво', 'price': 44300}, {'model': 'Фольксваген', 'price':
21300}]
Вот так фильтр
может кардинально менять поведение шаблона. В общем случае синтаксис фильтра sum, следующий:
sum(iterable,
attribute=None, start=0)
Здесь последний
параметр start – это
прибавочное значение к вычисленной сумме.
Полный список
доступных фильтров, следующий:
Они все вполне
очевидны и подробное описание можно почитать на странице официальной
документации:
https://jinja.palletsprojects.com/en/2.11.x/templates/
Я здесь приведу
лишь еще несколько примеров:
tpl = "Автомобиль: {{ cs | max(attribute='price') }}"
Выводит словарь
для автомобиля с максимальной ценой:
Автомобиль:
{'model': 'Вольво', 'price': 44300}
Для выбора
отдельного поля следует использовать такую запись:
tpl = "Автомобиль: {{ (cs | max(attribute='price')).model }}"
то есть, мы
сначала получаем словарь, а затем, выбираем из него поле model.
То же самое
будет и для фильтра min:
tpl = "Автомобиль: {{ (cs | min(attribute='price')).model }}"
Выбор случайного
значения из последовательности:
tpl = "Автомобиль: {{ cs | random }}"
Замена малой
буквы ‘о’ на заглавную:
tpl = 'Автомобиль: {{ cs | replace("о", "О") }}'
И так далее.
Каждый из этих фильтров можно применять в соответствии с его описанием и на
выходе будем получать соответствующие данные.
Блок filter
Большую гибкость
в применении фильтров дает специальный блок:
{{% filter <название фильтра> %}
<фрагмент для применения фильтра>
{% endfilter %}
Например,
сделать так:
persons = [
{"name": "Алексей", "old": 18, "weight": 78.5},
{"name": "Николай", "old": 28, "weight": 82.3},
{"name": "Иван", "old": 33, "weight": 94.0}
]
tpl = '''
{%- for u in users -%}
{% filter upper %}{{u.name}}{% endfilter %}
{% endfor -%}
'''
tm = Template(tpl)
msg = tm.render(users = persons)
print(msg)
На выходе
получим список имен, записанных заглавными буквами.
Макроопределения
Модуль Jinja поддерживает
макроопределения для шаблонов, которые весьма полезны, чтобы избежать
повторяемых определений в соответствии с принципом
DRY –
Don’t Repeat Yourself (не повторяйся)
Например, нам
необходимо создать несколько полей ввода input в шаблоне HTML-документа. Его
можно задать так:
html = '''
{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}">
{%- endmacro %}
{{ input('username') }}
{{ input('email') }}
{{ input('password') }}
'''
Здесь с помощью
ключевого слова macro задано макроопределение с именем input и набором
параметров. Это очень похоже на определение функций в Python. Учитывая, что
в качестве параметров можно указывать специальные:
-
varargs
– список переданных значений (параметров);
-
kwargs
– список переданных именованных параметров.
А далее, мы используем
этот макрос для создания трех полей input. Как видите,
это может быть очень удобно. По аналогии можно создавать другие макросы и,
затем, многократно их использовать в шаблонах.
Вложенные макросы – call
Модуль Jinja имеет
специальное определение:
{% call[(параметры)] <вызов макроса> %}
<вложенный шаблон>
{% endcall %}
которое
позволяет создавать своего рода вложенные макросы. Проще всего понять работу
этого блока на конкретном примере. Предположим, мы хотим сформировать вот такой
список:
На уровне HTML-документа это
выглядит так:
<ul>
<li>Алексей
<ul>
<li>age:
<li>weight: 78.5
</ul>
<li>Николай
<ul>
<li>age:
<li>weight: 82.3
</ul>
<li>Иван
<ul>
<li>age:
<li>weight: 94.0
</ul>
</ul>
Создадим шаблон,
который позволяет генерировать такой список на основе коллекции:
persons = [
{"name": "Алексей", "old": 18, "weight": 78.5},
{"name": "Николай", "old": 28, "weight": 82.3},
{"name": "Иван", "old": 33, "weight": 94.0}
]
Далее, определим
макрос, который генерирует начальный список из имен. Его можно представить так:
html = '''
{% macro list_users(list_of_user) -%}
<ul>
{% for u in users -%}
<li>{{u.name}}
{%- endfor %}
</ul>
{%- endmacro %}
{{list_users(users)}}
'''
tm = Template(html)
msg = tm.render(users = persons)
print(msg)
После выполнения
этой программы, увидим следующее:
<ul>
<li>Алексей<li>Николай<li>Иван
</ul>
Отлично, это
сделали. А теперь для каждого человека добавим вложенный список с его возрастом
и весом. И сделаем это через блок call. Определим его
так:
html = '''
{% macro list_users(list_of_user) -%}
<ul>
{% for u in list_of_user -%}
<li>{{u.name}} {{caller(u)}}
{%- endfor %}
</ul>
{%- endmacro %}
{% call(user) list_users(users) %}
<ul>
<li>age: {{user.old}}
<li>weight: {{user.weight}}
</ul>
{% endcall -%}
'''
Смотрите, мы
здесь прописали call с передаваемым ему параметром user – это будет
текущий словарь, взятый из списка persons. Далее,
указываем макрос, который следует вызвать для этого блока call. А все что
записано внутри этого блока будет подставлено на место вызова метода caller внутри макроса list_users. В результате
будет сформирован искомый список.