Статьи

Шаблонирование с Jinja2 во Flask: Advanced

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

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

Иногда нам может потребоваться вычислить или обработать значение непосредственно в шаблонах. Jinja2 придерживается мнения, что обработка логики должна обрабатываться в представлениях, а не в шаблонах, и, следовательно, поддерживает чистоту шаблонов. В этом случае контекстный процессор становится удобным инструментом. Мы можем передать наши значения методу; затем это будет обработано в методе Python, и будет возвращено наше результирующее значение. Поэтому мы, по сути, просто добавляем функцию в контекст шаблона (спасибо Python за то, что он позволяет нам передавать функции, как и любой другой объект).

Итак, допустим, мы хотим добавить описательное имя для каждого продукта в формате Category / Product-name . Для этого необходимо добавить метод, который должен быть украшен @app.context_processor .

1
2
3
4
5
@app.context_processor
def some_processor():
    def full_name(product):
        return ‘{0} / {1}’.format(product[‘category’], product[‘name’])
    return {‘full_name’: full_name}

Технически, контекст — это просто словарь Python, который можно изменять для добавления и удаления значений. Любой метод с указанным декоратором должен возвращать словарь, который обновит текущий контекст приложения.

Чтобы использовать этот контекстный процессор, просто добавьте следующий тег Jinja2 в шаблон.

1
<h4>{{ full_name(product) }}</h4>

Если мы добавим это в этот flask_app/templates/product.html нашего приложения, это будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
{% extends ‘home.html’ %}
 
{% block container %}
  <div class=»top-pad»>
    <h4>{{ full_name(product) }}</h4>
    <h1>{{ product[‘name’] }}
      <small>{{ product[‘category’] }}</small>
    </h1>
    <h3>$ {{ product[‘price’] }}</h3>
  </div>
{% endblock %}

Полученная страница продукта теперь будет выглядеть так:

Пример страницы с одним продуктом

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

1
2
3
@app.template_filter(‘full_name’)
def full_name_filter(product):
    return ‘{0} / {1}’.format(product[‘category’], product[‘name’])

Этот фильтр можно использовать так же, как обычный фильтр, т. Е. Путем добавления | (pipe) | (pipe) а затем имя фильтра.

1
{{ product|full_name }}

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

Чтобы поднять вещи на более высокий уровень, давайте создадим фильтр, который будет форматировать валюту на основе текущего языка браузера. Для этого сначала нам нужно установить пакет Python с именем ccy .

1
$ pip install ccy

Теперь нам нужно добавить метод для фильтра валют.

1
2
3
4
5
6
7
import ccy
from flask import request
 
@app.template_filter(‘format_currency’)
def format_currency_filter(amount):
    currency_code = ccy.countryccy(request.accept_languages.best[-2:])
    return ‘{0} {1}’.format(currency_code, amount)

Чтобы использовать этот фильтр, нам нужно добавить следующее в наш шаблон:

1
<h3>{{ product[‘price’]|format_currency }}</h3>

Теперь страница продукта будет выглядеть так:

Форматирование валюты на одной странице товара

Макросы позволяют нам писать фрагменты HTML-блоков многократного использования. Они аналогичны функциям в обычных языках программирования. Мы можем передавать аргументы макросам так же, как и функциям в Python, а затем использовать их для обработки блока HTML. Макросы можно вызывать любое количество раз, и выходные данные будут различаться в соответствии с логикой внутри них. Работа с макросами в Jinja2 является очень распространенной темой и имеет множество вариантов использования. Здесь мы просто увидим, как макрос может быть создан и затем использован после импорта.

Одним из наиболее избыточных фрагментов кода в HTML является определение полей ввода в формах. Большинство полей имеют похожий код с некоторыми изменениями стиля и так далее. Ниже приведен макрос, который создает поля ввода при вызове. _helpers.html создавать макрос в отдельном файле для лучшего повторного использования, например, _helpers.html :

1
2
3
{% macro render_field(name, class=», value=», type=’text’) -%}
    <input type=»{{ type }}» name=»{{ name }}» class=»{{ class }}» value=»{{ value }}»/>
{%- endmacro %}

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

1
{% from ‘_helpers.jinja’ import render_field %}

Затем его можно просто вызвать с помощью следующего:

1
2
3
4
<fieldset>
    {{ render_field(‘username’, ‘icon-user’) }}
    {{ render_field(‘password’, ‘icon-key’, type=’password’) }}
</fieldset>

Рекомендуется всегда определять макросы в другом файле, чтобы обеспечить чистоту кода и повысить читаемость кода. Если нужен закрытый макрос, к которому нельзя получить доступ из текущего файла, назовите макрос с подчеркиванием перед именем.

В этом уроке мы увидели, как написать собственный фильтр, пользовательский контекстный процессор и пользовательский макрос для форм. В следующей части этой серии мы увидим, как реализовать расширенное форматирование даты и времени на уровне шаблона в Jinja2 с помощью moment.js .