Веб-приложение состоит из веб-страниц. Даже если приложение реализует большую часть интерфейса пользователя с помощью JavaScript, домашняя страница все равно отдается сервером. Да и ситуации, когда страничный сайт хорошо решает поставленные задачи, возникают часто. Поэтому подсистема шаблонизации остается важной частью full-stack фреймворков.
В этом уроке разберем, что такое шаблонизация, и как на ее основе Django формирует HTML-страницы.
Язык шаблонизации
Шаблон — это почти готовый текстовый документ, в котором все неизменные части текста представлены как есть. А изменяемая часть описана на некотором языке с ветвлениями, условиями и циклами. Они позволяют преобразовать данные в подходящее представление. Этот язык называют языком шаблонизации или templating language.
На уровне шаблона разделяется ответственность. Шаблон страницы знает, как отобразить данные в виде HTML. А сами данные не зависят от этого представления и могут быть представлены по-разному разными шаблонами. По данным вообще нельзя сказать, что они будут представлены в виде HTML. Шаблонизатор возводит барьер абстракции.
Чтобы из кода, который формирует данные, не нужно было программировать отображение, у шаблонизатора есть свой язык программирования отображения. А для того, чтобы не было соблазна прямо в шаблоне получать данные, этот язык сделан с ограниченными возможностями.
Шаблонизация в Django
В Django шаблонизация проработана достаточно глубоко. Есть встроенный шаблонизатор, можно использовать сторонние шаблонизаторы вроде Jinja2 и можно сочетать несколько шаблонизаторов в одном проекте. Это удобно, когда некоторые шаблоны достаются в наследство, либо когда мы переезжаем с одного шаблонизатора на другой.
Чтобы шаблон превратить в результат, Django использует бэкенды. Встроенных бэкенда два: DjangoTemplates и Jinja2. Сторонние пакеты могут предоставлять свои бэкенды.
Чтобы бэкенд мог что-то сделать с шаблоном, нужно этот шаблон где-то взять. Django самостоятельно загружает шаблоны из файлов, знает, где эти файлы найти, и запоминает/кэширует часто используемые шаблоны в памяти. Нам остается только помнить имя шаблона, который хотим использовать в данный момент.
Настраивается это в settings.py
с помощью переменной TEMPLATES
. Выглядит настройка так:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': False,
'OPTIONS': {
# ... параметры шаблонизатора ...
},
},
]
DIRS
— параметр, который перечисляет директории, где будет производиться поиск шаблоновAPP_DIRS
— параметр, который разрешает Django искать шаблоны в директориях приложений
Когда APP_DIRS
включен, в каждом подключенном приложении будут по умолчанию искаться директории templates, а в них — шаблоны.
По умолчанию в качестве директории с шаблонами принято указывать templates:
# hexlet_django_blog/settings.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
# ... параметры шаблонизатора ...
},
]
Шаблоны приложений
У одного приложения в нашем учебном проекте уже есть шаблон. Речь идет о главном приложении hexlet_django_blog
и шаблоне hexlet_django_blog/templates/index.html
. Django находит и загружает этот шаблон, потому что опция APP_DIRS
включена по умолчанию.
Если сейчас создать новый шаблон hexlet_django_blog/article/templates/index.html
, то при поиске по имени index.html первым будет найден старый шаблон hexlet_django_blog/templates/index.html
. Так происходит, потому что приложение hexlet_django_blog
находится в списке settings.INSTALLED_APPS
выше приложения hexlet_django_blog.article
. Если переставить приложения местами, то загрузчик шаблонов учтет изменения.
С помощью управления порядком подключения приложений можно переопределять шаблоны одних приложений шаблонами из других. Это интересная возможность, но из-за нее же приходится следить за тем, чтобы шаблоны не перепутывались случайно.
Чтобы не думать слишком много над тем, как не путать шаблоны, стоит придерживаться правила:
Все шаблоны проекта, которые используются только в нем, стоит хранить в отдельной директории в корне проекта и держать эту директорию в порядке — использовать поддиректории и хорошие имена. Директорию нужно добавить в
settings.TEMPLATES.DIRS
.
Для тех же приложений, которые планируется использовать повторно, внутри соответствующей директории templates
нужно иметь поддиректорию с именем приложения и все шаблоны располагать в ней. Это уменьшит вероятность конфликтов имен шаблонов.
Контекст шаблонизации
Шаблоны превращаются в текст с помощью выполнения кода на языке шаблонизатора. Этот код обычно подразумевает использование каких-то данных, получаемых вне шаблона. Данные передаются в шаблон с помощью контекста, который представляет собой словарь. Когда мы вызываем render(request, 'template.html', context={})
, мы передаем этот самый словарь в качестве аргумента.
Часто возникает задача иметь в контексте шаблона доступ к параметрам запроса или настройкам проекта. Передавать такие вещи явно слишком утомительно, поэтому у Django есть механизм Context Processors — посредники между нами и шаблоном, которые расширяют контекст единым образом для всех шаблонов.
Процессоры контекста вызываются как функции, которые получают в качестве аргумента request
и возвращают словарь. Затем этот словарь дополняет предоставленный нами контекст. Подключаются context processors в settings.TEMPLATES.OPTIONS.context_processors
— это список строк с полными именами процессоров.
С Django поставляется много готовых процессоров контекста. Вот два примера:
django.template.context_processors.request
добавляет в контекст переменнуюrequest
с очевидным значениемdjango.template.context_processors.debug
добавляет переменнуюdebug
, которая истинна, если сервер запущен в режиме разработчика. С помощью этой переменной можно выводить какую-то отладочную информацию, которая не будет видна в боевом режиме
В дополнение к встроенным можно создавать и свои процессоры контекста или же брать их из сторонних библиотек.
Работа с шаблонизатором Django
Пример простого Django-шаблона:
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<pre>
{{ code }}
</pre>
<p>Question submitted at: {{ created_at }}</p>
Шаблон выглядит как HTML, но со вставками кода. Во многом встроенный Django шаблонизатор схож с шаблонизатором Jinja2 — подстановка любого значения выполняется в двойных фигурных скобках {{ ... }}
. Это очень похоже на интерполяцию, если сам шаблон рассматривать как строку.
Под капотом Django выполняет дополнительную обработку и экранирует любые данные, которые вставлены таким образом. Это значит, что нам не нужно прилагать дополнительные усилия, чтобы обеспечить безопасность в шаблонах.
Данные в шаблон поступают из вью в виде контекста:
# hexlet_django_blog/views.py
def about(request):
tags = ['обучение', 'программирование', 'python', 'oop']
return render(
request,
'about.html',
context={'tags': tags},
)
<!-- hexlet_django_blog/templates/about.html -->
<h1>О блоге</h1>
<p>Эксперименты с Django на Хекслете</p>
<p>{{ tags|join:', ' }}</p>
Двойные фигурные скобки позволяют не только выводить информацию, которую передали в контексте, но и производить ее простую обработку при помощи встроенных тегов и фильтров шаблонов. Главное правило — не злоупотреблять. В основном данные должны быть подготовлены в обработчике до того, как они попадут в шаблон. Тут нужно помнить: шаблоны — про отображение, а не про логику работы.
Кроме подстановки во встроенном шаблонизаторе есть инструкции. С их помощью реализованы все управляющие конструкции, например, циклы, условия и многое другое:
{% if records|length == 1 %}
I have one record!
{% endif %}
{% for user in users %}
<p>This is user {{ user.id }}</p>
{% endfor %}
Инструкции всегда оборачиваются в фигурные скобки со знаком процента {% ... %}
и часто имеют закрывающую часть. При помощи инструкций можно делать практически то же самое, что и в самом Python. В инструкциях, как и в подстановках, можно использовать переменные из контекста.
Простые конструкции вроде обращения по ключу, по индексу, по имени атрибута нам доступны. Поэтому контекст может содержать и сложносоставные сущности. А вот вызывать функции и методы уже не получится — это то самое отделение логики от представления.
Самостоятельная работа
- Добавьте для
hexlet_django_blog.article.views.index
отдельный шаблон, который поместите в приложенииhexlet_django_blog.article
в директориюtemplates/articles
- Выводите название приложения с помощью шаблона. Используйте подстановки, все значения передавайте через контекст
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.