Учебник по шаблонизации Jinja
Оглавление
- Начните работу с Джинджи
- Контролируйте поток в Джиндже
- Используйте Джинджу с Фляжкой
- Разместите свои шаблоны
- Применяйте фильтры
- Включать макросы
- Заключение
- Часто задаваемые вопросы
Jinja - это мощный движок шаблонов, обычно используемый в веб-приложениях на Python для создания динамических веб-страниц. Jinja также поддерживает автономное использование, позволяя вам создавать текстовые файлы с программно заполненным контентом, что делает его универсальным за пределами веб-фреймворков, таких как Flask и Django.
В этом руководстве вы узнаете, как установить Jinja, создавать и визуализировать шаблоны Jinja, а также использовать такие функции Jinja, как условные операторы и циклы. Вы также узнаете, как использовать фильтры и макросы для улучшения функциональности ваших шаблонов, а также узнаете, как вставлять шаблоны и легко интегрировать Jinja в веб-приложение Flask.
К концу этого урока вы поймете, что:
- Jinja используется для создания динамических веб-шаблонов и генерации текстовых файлов с программным содержимым.
- Механизм создания шаблонов обрабатывает шаблоны с динамическим содержимым, отображая их как статические страницы.
- Вы используете Шаблоны Jinja в HTML, встраивая динамические заполнители и отображая их с помощью Python.
- Вы создаете оператор
if-else
в Jinja, используя{% if condition %} ... {% else %} ... {% endif %}
. - Вы создаете
for
цикл в Jinja, используя{% for item in list %} ... {% endfor %}
. - Jinja в основном используется в Python, но может быть интегрирован с другими языками и фреймворками.
Вы начнете с самостоятельного использования Jinja для изучения основ создания шаблонов Jinja. Позже вы создадите базовый веб-проект Flask с двумя страницами и панелью навигации, чтобы использовать весь потенциал Jinja.
На протяжении всего урока вы будете создавать пример приложения, демонстрирующего некоторые из широкого спектра функций Jinja. Чтобы увидеть, что он будет делать, перейдите к заключительному разделу.
Начните работу с Jinja
Джинджа - это не только город в Восточном регионе Уганды и Японский храм, но также движок шаблонов. Обычно вы используете шаблонизаторы для веб-шаблонов, которые получают динамический контент из серверной части и визуализируют это как статическая страница во внешнем интерфейсе.
Но вы можете использовать Jinja и без веб-фреймворка, работающего в фоновом режиме. Именно это вы и будете делать в этом разделе. В частности, вы установите Jinja и создадите свои первые шаблоны.
Установить Jinja
Перед изучением любого нового пакета рекомендуется создать и активировать виртуальную среду. Таким образом, вы устанавливаете все зависимости проекта в виртуальной среде вашего проекта, а не в общесистемной.
Выберите свою операционную систему ниже и используйте команду, зависящую от вашей платформы, для настройки виртуальной среды:
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS>
$ python -m venv venv
$ source venv/bin/activate
(venv) $
С помощью приведенных выше команд вы создаете и активируете виртуальную среду с именем venv
, используя встроенный в Python модуль venv
. Круглые скобки (()
), окружающие venv
перед приглашением, указывают на то, что вы успешно активировали виртуальную среду.
После того, как вы создали и активировали свою виртуальную среду, пришло время установить Jinja с pip
:
(venv) $ python -m pip install Jinja2
Не забудьте поставить 2
в конце названия пакета. В противном случае вы установите старую версию, которая несовместима с Python 3.
Стоит отметить, что, хотя текущая основная версия на самом деле больше, чем 2
, пакет, который вы будете устанавливать, тем не менее, называется Jinja2
. Вы можете убедиться, что у вас установлена современная версия Jinja, выполнив pip list
:
(venv) $ python -m pip list
Package Version
---------- -------
Jinja2 3.x
...
Чтобы еще больше запутать ситуацию, после установки Jinja с заглавной буквой J
, вы должны импортировать ее со строчной буквой j
в Python. Попробуйте это, открыв интерактивный интерпретатор Python и выполнив следующие команды:
>>> import Jinja2
Traceback (most recent call last):
...
ModuleNotFoundError: No module named 'Jinja2'
>>> import jinja2
>>> # No error
Когда вы пытаетесь импортировать Jinja2
с именем в верхнем регистре, которое вы использовали для установки Jinja, у вас возникает ModuleNotFoundError
. Чтобы импортировать пакет Jinja в Python, вы должны ввести jinja2
со строчной буквы j
.
Отрисуйте свой первый шаблон Jinja
Импортировав Jinja, вы можете приступить к загрузке и рендерингу вашего первого шаблона:
>>> import jinja2
>>> environment = jinja2.Environment()
>>> template = environment.from_string("Hello, {{ name }}!")
>>> template.render(name="World")
'Hello, World!'
Основным компонентом Jinja является класс Environment()
. В этом примере вы создаете среду Jinja без каких-либо аргументов. Позже вы измените параметры Environment
, чтобы настроить свою среду. Здесь вы создаете простую среду, в которую загружаете строку "Hello, {{ name }}!"
в качестве шаблона.
То, что вы только что сделали, может показаться не намного более впечатляющим, чем использование форматированной строки в обычном Python. Однако в этом примере показаны два важных шага, которые вы обычно выполняете при использовании Jinja:
- Загрузите шаблон: Загрузите исходный код, содержащий переменные-заполнители. По умолчанию они заключены в пару двойных фигурных скобок (
{{ }}
). - Визуализируйте шаблон: Заполните заполнители содержимым. Вы можете предоставить словарь или аргументы ключевых слов в качестве контекста. В данном случае вы заполнили заполнитель так, чтобы на выходе получилось знакомое
Hello, World!
.
Загружаемый исходный шаблон может быть буквальной строкой. Но все становится намного интереснее, когда вы работаете с файлами и предоставляете текстовый файл в качестве шаблона.
Используйте внешний файл в качестве шаблона
Если вы хотите следовать примерам из этого руководства, то можете продолжить и создать новую папку для работы. Внутри вашего рабочего каталога создайте папку с именем templates/
.
Все новые шаблоны будут храниться в папке templates/
. Теперь создайте текстовый файл с именем message.txt
:
templates/message.txt
Hello {{ name }}!
I'm happy to inform you that you did very well on today's {{ test_name }}.
You reached {{ score }} out of {{ max_score }} points.
See you tomorrow!
Anke
Представьте, что вы учитель, который хочет отправить результаты ученикам с хорошей успеваемостью. Шаблон message.txt
содержит схему сообщения, которую вы можете скопировать и вставить для отправки позже. Как и в примере с Hello, World!
, в тексте вашего шаблона вы найдете фигурные скобки ({{ }}
).
Далее создайте файл Python с именем write_messages.py
:
write_messages.py
from jinja2 import Environment, FileSystemLoader
max_score = 100
test_name = "Python Challenge"
students = [
{"name": "Sandrine", "score": 100},
{"name": "Gergeley", "score": 87},
{"name": "Frieda", "score": 92},
]
environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("message.txt")
for student in students:
filename = f"message_{student['name'].lower()}.txt"
content = template.render(
student,
max_score=max_score,
test_name=test_name
)
with open(filename, mode="w", encoding="utf-8") as message:
message.write(content)
print(f"... wrote {filename}")
Когда вы создаете среду Jinja с помощью FileSystemLoader
, вы можете указать путь, который указывает на папку с вашими шаблонами. Вместо того, чтобы вводить строку, вы теперь загружаете message.txt
в качестве своего шаблона. Как только ваш шаблон будет загружен, вы сможете использовать его снова и снова, чтобы наполнить его содержимым. В write_messages.py
вы выводите name
и score
для каждого из ваших лучших учеников в текстовый файл.
Обратите внимание, что ключи словаря students
, а также max_score
и test_name
соответствуют переменным шаблона в message.txt
. Если вы не предоставляете контекст для переменных в шаблоне, они не выдают ошибку. Но они отображают пустую строку, что обычно нежелательно.
Когда вы вызываете template.render()
, вы возвращаете отображаемый шаблон в виде строки. Как и в случае с любой другой строкой, вы можете использовать .write()
для записи ее в файл. Чтобы увидеть write_messages.py
в действии, запустите скрипт:
(venv) $ python write_messages.py
... wrote message_sandrine.txt
... wrote message_gergeley.txt
... wrote message_frieda.txt
Вы только что создали файл для каждого из ваших учеников. Взгляните на message_gergeley.txt
, например:
message_gergeley.txt
Hello Gergeley!
I'm happy to inform you that you did very well on today's Python Challenge.
You reached 87 out of 100 points.
See you tomorrow!
Anke
Переменные вашего шаблона message.txt
успешно получили данные вашего ученика. Какой отличный способ заставить Python и Jinja работать на вас! Теперь вы можете скопировать и вставить текст, отправить его своим студентам и сэкономить себе время.
Контролируйте поток в Дзиндзя
До сих пор вы добавляли переменные-заполнители в текст шаблона и отображали в нем значения. В этом разделе вы узнаете, как добавлять инструкции if
и циклы for
в шаблон, чтобы условно отображать содержимое, не повторяясь.
Используйте операторы if
В примере из предыдущего раздела вы программно создали индивидуальные сообщения для своих лучших учеников. Теперь пришло время рассмотреть всех ваших учеников. Добавьте двух учащихся с более низкими баллами к students
в write_messages.py
:
write_messages.py
# ...
students = [
{"name": "Sandrine", "score": 100},
{"name": "Gergeley", "score": 87},
{"name": "Frieda", "score": 92},
{"name": "Fritz", "score": 40},
{"name": "Sirius", "score": 75},
]
# ...
Вы добавляете оценки Фрица и Сириуса в список students
. В отличие от других учеников, оба набрали менее восьмидесяти баллов. Используйте знак 80
для создания условного оператора в message.txt
:
templates/message.txt
1Hello {{ name }}!
2
3{% if score > 80 %}
4I'm happy to inform you that you did very well on today's {{ test_name }}.
5{% else %}
6I'm sorry to inform you that you did not do so well on today's {{ test_name }}.
7{% endif %}
8You reached {{ score }} out of {{ max_score }} points.
9
10See you tomorrow!
11Anke
В дополнение к переменным, которые вы использовали ранее, теперь вы также используете условный оператор с блоком Jinja . Вместо использования пары двойных фигурных скобок вы создаете блоки Jinja с одной фигурной скобкой и знаком процента ({% %}
) на каждом конце.
В то время как обычные переменные можно рассматривать как подстроки, блоки Jinja закрывают большую часть вашего шаблона. Вот почему вам также нужно указать Jinja, где заканчивается ваш блок. Чтобы закрыть блок, вы снова используете те же ключевые слова с префиксом end
.
В приведенном выше примере вы начинаете блок {% if %}
в строке 3, который вы закрываете в строке 7 с помощью {% endif %}
. Сам оператор if
работает как условных операторов в Python. В строке 3 вы проверяете, является ли score
больше, чем 80
. Если это так, то вы отправляете радостное сообщение. В противном случае вы отправляете сообщение с извинениями в строке 6.
В приведенном выше примере студенты согласились с тем, что они хотят получить восемьдесят баллов в качестве критерия для оценки своей успеваемости в Python challenge. Не стесняйтесь менять 80
на любой балл, который устроит вас и ваших студентов.
Рычаги для создания петель
Вы также можете управлять потоком ваших шаблонов с помощью for
циклов. В качестве примера вы решили создать HTML-страницу для своих студентов, на которой отображаются все результаты. Обратите внимание, что все студенты согласились открыто продемонстрировать свои результаты в этом дружеском соревновании.
Создайте новый файл с именем results.html
в вашем каталоге templates/
:
templates/results.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Results</title>
</head>
<body>
<h1>{{ test_name }} Results</h1>
<ul>
{% for student in students %}
<li>
<em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
</li>
{% endfor %}
</ul>
</body>
</html>
Здесь вы создаете HTML-страницу, которая просматривает ваш словарь students
и отображает их производительность. Так же, как и в случае с блоками {% if %}
, вы должны убедиться, что закрываете свой блок {% for %}
с помощью {% endfor %}
.
Вы можете комбинировать инструкции if
и циклы for
, чтобы еще больше управлять потоком ваших шаблонов:
templates/results.html
{# ... #}
{% for student in students %}
<li>
{% if student.score > 80 %}🙂{% else %}🙁{% endif %}
<em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
</li>
{% endfor %}
{# ... #}
В зависимости от оценки ученика, вы показываете смайлик с улыбающимся или грустным выражением лица. Обратите внимание, что выражения блоков также можно поместить в одну строку.
Продолжайте и обновляйте write_messages.py
:
write_messages.py
# ...
results_filename = "students_results.html"
results_template = environment.get_template("results.html")
context = {
"students": students,
"test_name": test_name,
"max_score": max_score,
}
with open(results_filename, mode="w", encoding="utf-8") as results:
results.write(results_template.render(context))
print(f"... wrote {results_filename}")
В дополнение к циклу for
, в котором вы сохраняли сообщение для каждого учащегося, теперь вы также создаете один HTML-файл, содержащий все результаты для этого учащегося. На этот раз вы создаете context
словарь, содержащий все переменные, которые вы передаете в шаблон.
Примечание: Использование context
в качестве имени коллекции, в которой хранятся переменные для шаблона, является условным. Тем не менее, вы можете назвать словарь по-другому, если хотите.
Со словарем, содержащим весь контекст вашего шаблона, вы можете вызвать .render()
с context
в качестве единственного аргумента. Когда вы запускаете write_messages.py
, вы также создаете HTML-файл:
(venv) $ python write_messages.py
... wrote message_sandrine.txt
... wrote message_gergeley.txt
... wrote message_frieda.txt
... wrote message_fritz.txt
... wrote message_sirius.txt
... wrote students_results.html
Вы можете просмотреть отрисованный HTML-файл в своем редакторе кода. Однако, поскольку вы уже работаете с HTML, вы также можете просмотреть файл в своем браузере:
Как и в скриптах на Python, вы можете управлять потоком шаблонов Jinja с помощью инструкции if
и циклов for
. В Jinja вы используете блоки для переноса содержимого. Когда вы используете блок for
, содержимое внутри этого блока отображается на каждом шаге цикла.
С помощью шаблонов вы можете создавать стандартные блоки для больших веб-сайтов, не дублируя свой интерфейсный код. Вот почему веб-фреймворки, такие как Flask, используют возможности шаблонов Jinja. В следующем разделе вы узнаете, как использовать Flask для отображения данных из серверной части на веб-страницах интерфейсной части.
Используйте Джинджу с Flask
Скорее всего, вы впервые услышали о Jinja, когда использовали веб-фреймворк, подобный Flask. И Jinja, и Flask поддерживаются Pallets Project, организацией, управляемой сообществом, которая занимается библиотеками Python, используемыми в веб-платформе Flask.
В этом разделе вы продолжите рассмотрение предыдущих примеров, создав базовое веб-приложение для своих студентов.
Установить Flask
Вы можете продолжить работу в том же каталоге и в той же виртуальной среде, которые вы создали в предыдущем разделе. Когда вы окажетесь в своей активной виртуальной среде, продолжайте и установите Flask:
(venv) $ python -m pip install flask
После установки Flash продолжайте создавать свой первый маршрут, чтобы убедиться, что Flask работает должным образом. Создайте файл с именем app.py
в корневом каталоге вашего проекта:
app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello, World!"
if __name__ == "__main__":
app.run(debug=True)
Когда вы помещаете @app.route()
decorator поверх функции Flask view, вы регистрируете ее в задан URL-адрес правило. Здесь вы задаете маршрут /
, который возвращает Hello, World!
.
Чтобы увидеть свою домашнюю страницу в браузере, запустите веб-сервер разработки Flask:
(venv) $ python app.py
...
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
* Restarting with watchdog (fsevents)
* Debugger is active!
Теперь ваше приложение Flask работает в режиме debug
. В режиме debug
вы будете получать более содержательные сообщения об ошибках, если что-то пойдет не так. Кроме того, ваш сервер будет автоматически перезапускаться всякий раз, когда вы что-то меняете в своей кодовой базе.
Чтобы просмотреть свою домашнюю страницу, посетите http://127.0.0.1:5000
:
Потрясающе, теперь у вас есть работающее приложение Flask! В следующем разделе вы будете внедрять шаблоны Jinja в свое приложение Flask.
Добавить базовый шаблон
Пока что ваше приложение Flask возвращает строку. Вы могли бы улучшить свою строку, добавив HTML-код, и Flask отрисовал бы ее за вас. Но, как вы узнали из предыдущего раздела, использование шаблонов значительно упрощает отрисовку контента.
Создайте новый шаблон с именем base.html
в вашем каталоге templates/
:
templates/base.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <title>{{ title }}</title>
6</head>
7
8<body>
9 <h1>Welcome to {{ title }}!</h1>
10</body>
11</html>
В base.html
у вас есть два случая, когда вы используете переменные шаблона: один раз в строке 5, а затем еще раз в строке 9. Чтобы отобразить и обслуживать base.html
, загрузите его как свою домашнюю страницу в app.py
:
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("base.html", title="Jinja and Flask")
# ...
По умолчанию Flask ожидает, что ваши шаблоны будут находиться в каталоге templates/
. Таким образом, вам не нужно явно указывать каталог шаблонов. Когда вы указываете от base.html
до render_template()
, Flask знает, где искать ваш шаблон.
Примечание: Обычно вы улучшаете внешний вид своего веб-сайта с помощью CSS и добавляете некоторую функциональность с помощью JavaScript. Поскольку в этом руководстве вы сосредоточитесь на структуре контента, ваше веб-приложение в основном не будет стилизовано.
Перезапустите сервер разработки Flask, если он еще не обновился автоматически. Затем перейдите на страницу http://127.0.0.1:5000
и убедитесь, что Flask поддерживает и отображает ваш базовый шаблон:
Flask отобразил переменную title
в заголовке вашего веб-сайта и в приветственном сообщении. Далее вы создадите страницу, на которой будут отображаться результаты ваших учеников.
Добавить еще одну страницу
В одном из предыдущих разделов вы использовали results.html
в качестве шаблона для создания файла с именем students_results.html
. Теперь, когда у вас есть веб-приложение, вы можете использовать results.html
для динамического отображения вашего шаблона, на этот раз не сохраняя его в новый файл.
Убедитесь, что results.html
помещен в templates/
и выглядит следующим образом:
templates/results.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <title>{{ title }}</title>
6</head>
7
8<body>
9 <h1>{{ test_name }} {{ title }}</h1>
10 <ul>
11 {% for student in students %}
12 <li>
13 {% if student.score > 80 %}🙂{% else %}🙁{% endif %}
14 <em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
15 </li>
16 {% endfor %}
17 </ul>
18</body>
19</html>
Единственные изменения, внесенные в предыдущую версию, выделены выше:
- Строка 5 добавляет динамический заголовок страницы.
- Строка 9 дополняет первый заголовок.
Чтобы получить доступ к странице результатов в вашем веб-приложении, вы должны создать маршрут. Добавьте следующий код в app.py
:
app.py
# ...
max_score = 100
test_name = "Python Challenge"
students = [
{"name": "Sandrine", "score": 100},
{"name": "Gergeley", "score": 87},
{"name": "Frieda", "score": 92},
{"name": "Fritz", "score": 40},
{"name": "Sirius", "score": 75},
]
@app.route("/results")
def results():
context = {
"title": "Results",
"students": students,
"test_name": test_name,
"max_score": max_score,
}
return render_template("results.html", **context)
# ...
В полноценном веб-приложении вы, вероятно, хранили бы данные в внешней базе данных. На данный момент вы сохраняете max_score
, test_name
, и students
рядом с results()
.
Функция render_template()
Flask принимает только один позиционный аргумент, который является именем шаблона. Все остальные аргументы должны быть ключевыми словами. Итак, вам нужно распаковать свой словарь с двумя звездочками (**
) перед context
. С помощью операторов asterisk вы передаете элементы context
в качестве аргументов ключевого слова в render_template()
.
Когда вы посещаете http://127.0.0.1:5000/results
в своем браузере, Flask отображает results.html
с отображаемым контекстом. Перейдите в браузер и взгляните на это:
Теперь у вас есть домашняя страница и страница, на которой отображаются результаты ваших учеников. Это отличное начало для веб-приложения!
В следующем разделе вы узнаете, как еще больше использовать возможности ваших шаблонов, вкладывая их друг в друга. Вы также добавите в свой проект навигационное меню, чтобы пользователям было удобно переходить со страницы на страницу.
Вставляйте свои шаблоны
По мере роста вашего приложения и добавления новых шаблонов необходимо синхронизировать общий код. Пока что два ваших шаблона, base.html
и results.html
, выглядят очень похоже. Если несколько шаблонов содержат один и тот же код, вам необходимо скорректировать каждый шаблон, если вы изменяете какой-либо общий код.
В этом разделе вы реализуете структуру родительского и дочернего шаблонов, которая сделает ваш код намного более удобным в обслуживании.
Измените Свой Базовый шаблон
Когда вы используете наследование шаблонов Jinja, вы можете перенести общую структуру вашего веб-приложения в родительский базовый шаблон и разрешить дочерним шаблонам унаследуйте этот код.
Ваш base.html
почти готов к использованию в качестве базового шаблона. Чтобы сделать базовый шаблон расширяемым, добавьте в структуру несколько тегов {% block %}
:
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}{{ title }}{% endblock title %}</title>
</head>
<body>
{% block content %}
<h1>Welcome to {{ title }}!</h1>
{% endblock content %}
</body>
</html>
Вы используете тег {% block %}
, чтобы определить, какие части вашего базового шаблона могут быть переопределены дочерними шаблонами. Так же, как и в случае с {% if %}
и {% for %}
, вы должны закрыть свои блоки с помощью {% endblock %}
.
Обратите внимание, что вы также даете названия своим блокам. С помощью аргумента title
вы разрешаете дочернему шаблону заменять код между {% block title %}
и {% endblock title %}
своим собственным блоком title
. Вы можете заменить код {% block content %}
на блок content
.
Примечание: Названия ваших блоков контента должны быть уникальными в каждом шаблоне. В противном случае Jinja запутается, какой блок выбрать для замены.
Содержимое, которое находится между тегами {% block %}
в base.html
, является заполнителем. Резервное содержимое отображается всякий раз, когда дочерний шаблон не содержит соответствующих тегов {% block %}
.
Вы также можете отказаться от добавления резервного содержимого между тегами {% block %}
. Как и в случае с переменными в шаблонах, Jinja не будет жаловаться, если вы не предоставите для них содержимое. Вместо этого Jinja отобразит пустую строку.
В base.html
вы предоставляете резервное содержимое для блоков вашего шаблона. Таким образом, вам не нужно ничего менять в вашем представлении home()
. Это будет работать так же, как и раньше.
В следующем разделе вы подготовите свой дочерний шаблон для работы с ним base.html
.
Расширение дочерних шаблонов
В данный момент у вас есть шаблон results.html
, который работает независимо, без родительского шаблона. Это означает, что теперь вы можете изменить код results.html
, чтобы связать его с base.html
:
templates/results.html
1{% extends "base.html" %}
2
3{% block content %}
4<h1>{{ test_name }} {{ title }}</h1>
5<ul>
6{% for student in students %}
7 <li>
8 {% if student.score > 80 %}🙂{% else %}🙁{% endif %}
9 <em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
10 </li>
11{% endfor %}
12</ul>
13{% endblock content %}
Чтобы связать дочерний шаблон с его родительским шаблоном, вы должны добавить тег {% extends %}
в верхней части файла.
Дочерние шаблоны также содержат теги {% block %}
. Указывая имя блока в качестве аргумента, вы соединяете блоки из дочернего шаблона с блоками из родительского шаблона.
Обратите внимание, что results.html
не содержит блока title
. Но на вашей странице все равно будет отображаться правильный заголовок, потому что он использует резервное содержимое в base.html
, а в представлении предусмотрена переменная title
.
Вам не нужно настраивать свой режим просмотра results()
. При посещении http://127.0.0.1:5000/results
вы не должны заметить никаких изменений. Отображаемая страница содержит корневой код base.html
и поля для заполнения из блока results.html
.
Имейте в виду, что любой контент, не входящий в блоки дочернего шаблона, не будет отображаться на вашей отрисованной странице. Например, если вы хотите добавить в results.html
навигацию, которая ведет на вашу домашнюю страницу, вы должны либо определить новый блок-заполнитель в base.html
, либо добавить навигационное меню в структуру base.html
.
Включить навигационное меню
Навигация по веб-сайту обычно отображается на каждой странице. Учитывая структуру вашего базового и дочернего шаблонов, лучше всего добавить код для навигационного меню в base.html
.
Вместо того, чтобы добавлять код навигационного меню непосредственно в base.html
, вы можете использовать тег {% include %}
. Ссылаясь на другой шаблон с помощью {% include %}
, вы загружаете весь шаблон в эту позицию.
Включенные шаблоны являются частичными, которые содержат часть полного HTML-кода. Чтобы указать, что шаблон предназначен для включения, вы можете добавить к его названию символ подчеркивания (_
).
Следуйте схеме именования на основе префикса и создайте новый шаблон с именем _navigation.html
в вашей папке templates/
:
templates/_navigation.html
<nav>
{% for menu_item in ["home", "results"] %}
<a href="{{ url_for(menu_item) }}">{{ menu_item }}</a>
{% endfor %}
</nav>
Обратите внимание, что _navigation.html
не содержит ни тега {% extends %}
, ни каких-либо тегов {% block %}
. Вы можете сосредоточиться исключительно на том, как вы хотите отобразить свое навигационное меню.
Когда вы используете url_for()
, Flask создает для вас полный URL-адрес данного представления. Таким образом, даже если вы решите изменить маршрут к одной из своих страниц, навигационное меню все равно будет работать.
Включите _navigation.html
в base.html
, чтобы отобразить навигационное меню на всех ваших страницах:
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}{{ title }}{% endblock title %}</title>
</head>
<body>
<header>
{% include "_navigation.html" %}
</header>
{% block content %}
<h1>Welcome to {{ title }}!</h1>
{% endblock content %}
</body>
</html>
Вместо того, чтобы добавлять код навигационного меню непосредственно в base.html
, вы добавляете _navigation.html
в заголовок своего веб-сайта. Поскольку results.html
расширяет base.html
, вы можете посетить http://127.0.0.1:5000/results
, чтобы ознакомиться с новым навигационным меню:
Ваша страница результатов наследует код из вашего базового шаблона. При нажатии на ссылку в навигационном меню URL-адрес в адресной строке изменяется в зависимости от текущей страницы.
Применение фильтров
Найдите минутку и подумайте о ситуации, когда другой человек заботится о серверной части, а вы отвечаете за внешний интерфейс веб-сайта. Чтобы не мешать друг другу, вам не разрешается настраивать какие-либо представления в app.py
или изменять данные, которые поступают в ваши шаблоны.
После беседы со студентами вы соглашаетесь с тем, что ваше веб-приложение можно улучшить. Вы предлагаете две функции:
- Пункты навигационного меню отображаются в верхнем регистре.
- Отсортируйте имена ваших учеников в
results.html
алфавитном порядке.
Вы будете использовать функциональность фильтра от Jinja, чтобы реализовать обе функции, не затрагивая серверную часть.
Измените свои пункты меню
Jinja предоставляет множество встроенных фильтров. Если вы ознакомитесь с ними, то заметите, что они похожи на Встроенные функции Python и строковые методы.
Прежде чем продолжить, просмотрите свой _navigation.html
фрагмент:
templates/_navigation.html
<nav>
{% for menu_item in ["home", "results"] %}
<a href="{{ url_for(menu_item) }}">{{ menu_item }}</a>
{% endfor %}
</nav>
В настоящее время пункты вашего меню отображаются строчными буквами, чтобы соответствовать названию представления. Если бы menu_item
не были строчными, ссылка не работала бы. Это означает, что menu_item
в атрибуте href
должно оставаться как есть.
Однако вы можете настроить отображение menu_item
внутри тегов <a>
:
templates/_navigation.html
<nav>
{% for menu_item in ["home", "results"] %}
<a href="{{ url_for(menu_item) }}">{{ menu_item|upper }}</a>
{% endfor %}
</nav>
Вы указываете переменную, а затем символ канала (|
), за которым следует фильтр. В некоторых случаях вы можете указать аргументы в круглых скобках.
В вашем навигационном меню вы передаете переменную menu_item
фильтру upper
. Как и строковый метод Python .upper()
, фильтр Jinja upper
возвращает переменную в верхнем регистре.
Посетите http://127.0.0.1:5000
, чтобы увидеть свои изменения в реальном времени:
Отлично, пункты меню теперь написаны заглавными буквами! Вы реализовали первую функцию в своем списке запросов. Пора заняться следующим запросом.
Отсортируйте свой список результатов
На данный момент результаты ваших учеников отображаются в том же порядке, в каком вы определили их в словаре в app.py
. Вы будете использовать фильтр Jinja sort
, чтобы отсортировать список результатов в алфавитном порядке по учащимся.
Откройте results.html
и добавьте фильтр sort
в свой цикл for
:
templates/results.html
{# ... #}
{% for student in students|sort(attribute="name") %}
<li>
{% if student.score > 80 %}🙂{% else %}🙁{% endif %}
<em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
</li>
{% endfor %}
{# ... #}
Фильтр sort
использует кодировку Python sorted()
под капотом.
Ваш students
, повторяемый в results.html
, содержит словарь для каждого элемента. Добавляя attribute=name
, вы указываете Jinja отсортировать students
на основе значений name
. Вы можете использовать фильтр sort
без каких-либо аргументов, когда хотите отсортировать списки строк.
Перейдите к http://127.0.0.1:5000/results
и ознакомьтесь с новой сортировкой:
Вы использовали фильтр Jinja sort
, чтобы отсортировать результаты ваших учеников по их именам. Если у вас были ученики с одинаковыми именами, вы могли бы объединить фильтры в цепочку:
{% for student in students|sort(attribute="name")
|sort(attribute="score", reverse=true) %}
{# ... #}
{% endfor %}
Сначала вы сортируете своих учеников по именам. Если есть два ученика с одинаковыми именами, вы сортируете их по баллам. Конечно, учащиеся с одинаковыми именами на самом деле нуждались бы в некоторой дифференциации, помимо их оценок, но для целей примера такой сортировки достаточно.
Вы меняете порядок, указанный в score
, на противоположный, чтобы отсортировать учащихся от самых высоких баллов к самым низким. Если ваши реплики становятся слишком длинными, Jinja позволяет распределить ваши утверждения по нескольким строкам.
Примечание: В отличие от использования логических значений в Python, в Jinja логические значения следует писать строчными буквами.
Вы используете фильтры для преобразования данных в ваших шаблонах независимо от вашей серверной части. Применяя фильтр к переменной, вы можете изменить значение переменной для пользователя, не изменяя какую-либо базовую структуру данных.
Ознакомьтесь с Документацией по фильтрам Jinja, чтобы узнать больше о шаблонных фильтрах.
Если вы хотите добавить в свои шаблоны еще больше логики, то можете использовать макросы. В следующем разделе вы познакомитесь с макросами, реализовав еще три функции в своем приложении Flask.
Включить макросы
При включении частичных шаблонов, таких как навигационное меню, включенный код отображается в контексте родительского шаблона без необходимости каких-либо корректировок. Очень часто это именно то, чего вы хотите, но в других случаях вам может потребоваться настроить внешний вид ваших включенных шаблонов.
Макросы Jinja могут помочь вам создавать элементы шаблона, которые принимают аргументы. Как и при определении ваших собственных функций в Python, вы можете определять макросы и импортировать их в свои шаблоны.
В этом разделе вы добавите еще три функции в свой проект Flask:
- Установите темный режим.
- Выделите ученика с лучшим результатом.
- Отметьте текущую страницу в навигационном меню.
Как и прежде, вы не будете касаться какого-либо внутреннего кода для улучшения вашего веб-приложения.
Реализуйте темный режим
Для некоторых учащихся цветовая схема "светлое на темном" выглядит визуально более привлекательной. Чтобы удовлетворить потребности всех учащихся, вы добавите возможность переключения в темный режим со светлым текстом на темном фоне.
Добавьте файл macros.html
в каталог templates/
:
templates/macros.html
1{% macro light_or_dark_mode(element) %}
2 {% if request.args.get('mode') == "dark" %}
3 <a href="{{ request.path }}">Switch to Light Mode</a>
4 <style>
5 {{ element }} {
6 background-color: #212F3C;
7 color: #FFFFF0;
8 }
9 {{ element }} a {
10 color: #00BFFF !important;
11 }
12 </style>
13 {% else %}
14 <a href="{{ request.path }}?mode=dark">Switch to Dark Mode</a>
15 {% endif %}
16{% endmacro %}
Вы определяете макрос с помощью блока {% macro %}
, который напоминает определение функции в Python. У вашего макроса должно быть имя, и он может принимать аргументы.
Для макроса light_or_dark_mode()
необходимо указать имя HTML-элемента. Это будет элемент, который CSS в строках с 4 по 12 изменит со светлого на темный.
Чтобы избежать использования веб-сайта с темной тематикой по умолчанию, вы хотите предоставить своим студентам возможность изменять дизайн. Когда они добавляют ?mode=dark
к любому маршруту вашего приложения, вы активируете темный режим.
В строке 2 вы используете request
объект Flask для чтения параметров запроса. Объект request
присутствует в контексте вашего шаблона по умолчанию.
Если в запросе GET существует параметр темного режима, то вы показываете ссылку с возможностью переключения в светлый режим в строке 3 и добавляете тег style
в шаблон. Без каких-либо параметров темного режима вы показываете ссылку, которая переключает на темную тему.
Чтобы использовать ваши макросы, вы должны импортировать их в свой базовый шаблон. Так же, как и в случае с инструкциями import
в Python, рекомендуется поместить блок {% import %}
в начало вашего шаблона:
templates/base.html
{% import "macros.html" as macros %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}{{ title }}{% endblock title %}</title>
</head>
<body>
{# ... #}
<footer>
{{ macros.light_or_dark_mode("body") }}
</footer>
</body>
</html>
В зависимости от параметров GET запроса, вы отображаете темный режим и показываете ссылку для переключения на другую цветовую тему.
Указав body
в качестве аргумента для macros.light_or_dark_mode()
, ваши ученики могут изменить цветовую гамму всей страницы. Посетите страницу http://127.0.0.1:5000
и поиграйте с переключателем цветовой схемы.
Вы можете попробовать указать "h1"
вместо "body"
на macros.light_or_dark_mode()
, а затем перезагрузить страницу. Поскольку макросы принимают аргументы, они дают вам гибкость при условном отображении частей ваших шаблонов.
Выделите своего лучшего ученика
Еще одна причина для внедрения макросов в ваш проект Flask заключается в том, чтобы выделить некоторую логику в отдельный блок. Как и во вложенных шаблонах, передача функциональности макроса на аутсорсинг может привести в порядок ваш родительский шаблон.
Чтобы выделить своего лучшего ученика эмодзи в виде звездочки, очистите results.html
и используйте макрос add_badge()
:
templates/results.html
{# ... #}
{% for student in students|sort(attribute="name") %}
<li>
{{ macros.add_badge(student, students) }}
<em>{{ student.name }}:</em> {{ student.score }}/{{ max_score }}
</li>
{% endfor %}
{# ... #}
Обратите внимание, что вы не импортируете macros.html
в начало results.html
. Вы расширяете base.html
, куда вы уже импортировали все макросы. Так что нет необходимости импортировать их сюда снова.
Вместо добавления дополнительного кода к элементу списка в results.html
, вы ссылаетесь на macros.add_badge()
внутри вашего цикла for
. Вы можете воспользоваться этой возможностью, чтобы удалить условие if
... else
, при котором отображался смайлик с веселым или грустным выражением лица. Этот код идеально подходит для использования в макросе, а именно для добавления значка всем учащимся.
Макрос add_badge()
ожидает два аргумента:
- Текущий
student
словарь - Полный
students
список
Если вы сейчас зайдете на свою страницу, то получите сообщение об ошибке, потому что Flask не может найти макрос, на который вы ссылаетесь. Продолжайте и добавьте свой новый макрос в macros.html
:
templates/macros.html
1{# ... #}
2
3{% macro add_badge(student, students) %}
4 {% set high_score = students|map(attribute="score")|max %}
5
6 {% if student.score == high_score %}
7 ⭐️
8 {% elif student.score > 80 %}
9 🙂
10 {% else %}
11 🙁
12 {% endif %}
13{% endmacro %}
Jinja позволяет вам определять свои собственные переменные внутри шаблона с помощью блока {% set %}
. При определении переменных вы также можете добавлять фильтры к их значениям и даже связывать их в цепочки.
В add_badge
вы определяете high_score
, сначала создавая список всех оценок с помощью фильтра map()
, а затем выбирая наивысшую оценку с помощью max()
. Оба фильтра работают аналогично в Python map()
или max()
функция.
Как только вы узнаете самый высокий балл среди ваших учеников, вы сверяете его с оценкой вашего текущего ученика в строках с 6 по 12.
Перейдите на страницу http://127.0.0.1:5000/results
и посмотрите на свой новый макрос в действии:
В дополнение к эмодзи с улыбающимся или грустным лицом, которые вы показывали ранее, теперь вы показываете эмодзи со звездочкой для вашего самого успевающего ученика.
Отметить текущую страницу
Последняя функция, которую вы внедрите, улучшит ваше навигационное меню. На данный момент навигационное меню остается неизменным на обеих страницах. В этом разделе вы создадите макрос, который помечает пункт меню вашей текущей страницы стрелкой.
Добавьте еще один макрос в macros.html
:
templates/macros.html
{# ... #}
{% macro nav_link(menu_item) %}
{% set mode = "?mode=dark" if request.args.get("mode") == "dark" else "" %}
<a href="{{ url_for(menu_item) }}{{ mode }}">{{ menu_item|upper }}</a>
{% if request.endpoint == menu_item %}
←
{% endif %}
{% endmacro %}
Ваш макрос nav_link()
принимает элемент прейскуранта в качестве аргумента. Если menu_item
соответствует текущей конечной точке, то ваш макрос отображает стрелку в дополнение к ссылке на элемент прейскуранта.
Кроме того, вы проверяете наличие цветового режима. Если "dark"
является частью запроса GET, то в меню будет добавлена ссылка "?mode=dark"
. Без проверки и добавления режима вы бы переключались на световую тему каждый раз, когда нажимаете на ссылку, потому что "?mode=dark"
не было бы частью ссылки.
Замените ссылку на пункт меню в _navigation.html
вашим новым макросом:
templates/_navigation.html
<nav>
{% for menu_item in ["home", "results"] %}
{{ macros.nav_link(menu_item) }}
{% endfor %}
</nav>
Добавляя макрос nav_link()
в свое навигационное меню, вы поддерживаете чистоту своего навигационного шаблона. Вы передаете любую условную логику в nav_link()
.
Посетите http://127.0.0.1:5000
и ознакомьтесь со всеми функциями, которые вы внедрили.
Макросы - мощная функция Jinja. Тем не менее, не стоит злоупотреблять ими. В некоторых ситуациях, возможно, было бы лучше перенести логику в серверную часть, вместо того чтобы позволять шаблонам выполнять всю работу.
Всегда будут возникать крайние случаи, когда вы должны решить, добавлять ли код непосредственно в свой шаблон, перенести его во включенный шаблон или вместо этого создать макрос. Если в вашем шаблонном коде слишком много проблем со структурами данных, это может быть признаком того, что логика вашего кода относится к серверной части вашего приложения.
Заключение
Jinja - это многофункциональный движок для создания шаблонов, который поставляется в комплекте с веб-платформой Flask. Но вы также можете использовать Jinja независимо от Flask для создания шаблонов, которые вы можете программно наполнять контентом.
В этом руководстве вы узнали, как:
- Установите движок шаблонов Jinja
- Создайте свой первый шаблон Дзиндзя
- Визуализация шаблона Jinja в Flask
- Используйте
for
циклы и условные операторы с Jinja - Гнездо Шаблоны Джинджа
- Измените переменные в Jinja с помощью фильтров
- Используйте макросы для добавления функциональности в ваш интерфейс
Если вы хотите применить свои недавно полученные знания о Jinja на практике, то вы можете создать интерфейс для средства сокращения URL-адресов FastAPI или развернуть ваше приложение Flask на Герой.
Вы также можете рассмотреть возможность использования Jinja в качестве шаблонизатора для Django. Чтобы узнать больше о различиях между Jinja и движком шаблонизаторов Django, ознакомьтесь с документацией Jinja о переходе с других движков шаблонизаторов на Jinja.
Есть ли у вас другие варианты использования, в которых вы используете возможности, предоставляемые Jinja? Поделитесь ими с реальным сообществом Python в комментариях ниже!
Часто задаваемые вопросы
Теперь, когда у вас есть некоторый опыт работы с шаблонами Jinja в Python, вы можете использовать вопросы и ответы, приведенные ниже, чтобы проверить свое понимание и резюмировать то, что вы узнали.
- Вы используете Jinja для создания шаблонов, которые динамически генерируют HTML или другие текстовые форматы путем объединения статического содержимого с данными из вашего приложения на Python.
- Механизм создания шаблонов позволяет создавать документы, комбинируя статические шаблоны с динамическими данными, что позволяет программно создавать веб-страницы или другой текстовый контент.
- Вы создаете инструкцию
if-else
в Jinja, используя теги{% if condition %}
и{% else %}
, за которыми следует{% endif %}
, чтобы закрыть блок. - Вы создаете цикл
for
в Jinja, используя тег{% for item in items %}
, за которым следует{% endfor %}
, чтобы закрыть блок цикла. - Хотя Jinja в основном используется с Python, особенно с веб-фреймворками, такими как Flask или Django, это универсальный механизм создания шаблонов, который вы можете использовать в любом контексте, где вам нужно генерировать текстовый контент.