Язык шаблонов Django
В этом документе объясняется синтаксис языка системы шаблонов Django. Если вы ищете более техническую перспективу того, как она работает и как ее расширить, см. Язык шаблонов Django: для программистов Python.
Язык шаблонов Django разработан для достижения баланса между мощностью и простотой. Он предназначен для удобства работы с HTML. Если у вас есть опыт работы с другими текстовыми языками шаблонов, такими как Smarty или Jinja2, вам должно быть удобно с шаблонами Django.
Философия
Если у вас есть опыт программирования или вы привыкли к языкам, которые смешивают код программы непосредственно в HTML, помните, что система шаблонов Django не просто встраивает Python в HTML. Это сделано намеренно: система шаблонов предназначена для выражения представления, а не логики программы.
Система шаблонов Django предоставляет теги, которые функционируют аналогично некоторым конструкциям программирования — тег if для логических тестов, тег for для циклов и т. д. — но они не просто выполняются как соответствующий Python-код, и система шаблонов не будет выполнять произвольные Python-выражения. Только теги, фильтры и синтаксис, указанные ниже, поддерживаются по умолчанию (хотя вы можете добавить собственные расширения к языку шаблонов по мере необходимости).
Шаблоны
Шаблон — это текстовый файл. Он может генерировать любые текстовые форматы (HTML, XML, CSV и т. д.).
Шаблон содержит переменные, которые заменяются значениями при вычислении шаблона, и теги, которые управляют логикой шаблона.
Ниже приведен минимальный шаблон, который иллюстрирует несколько основ. Каждый элемент будет объяснен позже в этом документе.
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2>
<a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
Философия
Почему использовать текстовый шаблон вместо XML-шаблона (например, Zope’s TAL)? Мы хотели, чтобы язык шаблонов Django был полезен не только для XML/HTML-шаблонов. Вы можете использовать язык шаблонов для любых текстовых форматов, таких как электронные письма, JavaScript и CSV.
Переменные
Переменные выглядят так: {{ variable }}. Когда движок шаблонов встречает переменную, он вычисляет эту переменную и заменяет ее результатом. Имена переменных состоят из любой комбинации буквенно-цифровых символов и знака подчеркивания ("_"), но не могут начинаться с подчеркивания и не могут быть числом. Точка (".") также появляется в разделах переменных, хотя это имеет специальное значение, как указано ниже. Важно, что вы не можете использовать пробелы или знаки препинания в именах переменных.
Используйте точку (.), чтобы получить доступ к атрибутам переменной.
За кулисами
Технически, когда система шаблонов встречает точку, она пытается выполнить следующие действия в указанном порядке:
- Поиск по словарю
- Поиск атрибута или метода
- Поиск по числовому индексу
Если полученное значение является вызываемым, оно вызывается без аргументов. Результат вызова становится значением шаблона.
Этот порядок поиска может привести к некоторым неожиданным результатам с объектами, которые переопределяют поиск по словарю. Например, рассмотрите следующий фрагмент кода, который пытается перебрать collections.defaultdict:
{% for k, v in defaultdict.items %}
Do something with k and v here...
{% endfor %}
Поскольку поиск по словарю происходит первым, эта функция вступает в действие и предоставляет значение по умолчанию вместо использования предназначенного .items() метода. В этом случае следует сначала преобразовать в словарь.
В приведенном выше примере {{ section.title }} будет заменено атрибутом title объекта section.
Если вы используете переменную, которой нет, система шаблонов вставит значение параметра string_if_invalid, которое по умолчанию установлено в '' (пустая строка).
Обратите внимание, что «bar» в выражении шаблона, например, {{ foo.bar }}, будет интерпретироваться как литеральная строка, а не как значение переменной «bar», если она существует в контексте шаблона.
К атрибутам переменных, начинающимся с подчеркивания, нельзя получить доступ, так как они обычно считаются закрытыми.
Фильтры
Вы можете изменить переменные для отображения, используя фильтры.
Фильтры выглядят так: {{ name|lower }}. Это отображает значение переменной {{ name }} после фильтрации через фильтр lower, который преобразует текст в нижний регистр. Используйте знак «|» (|), чтобы применить фильтр.
Фильтры могут быть «сцеплены». Выход одного фильтра применяется к следующему. {{ text|escape|linebreaks }} — распространенное выражение для экранирования содержимого текста и преобразования символов перевода строки в теги <p>.
Некоторые фильтры принимают аргументы. Аргумент фильтра выглядит так: {{
bio|truncatewords:30 }}. Это отобразит первые 30 слов переменной bio.
Аргументы фильтров, содержащие пробелы, должны быть заключены в кавычки; например, чтобы объединить список запятыми и пробелами, вы используете {{ list|join:", " }}.
Django предоставляет около шестидесяти встроенных фильтров шаблонов. Вы можете прочитать обо всех них в справке по встроенным фильтрам. Чтобы дать вам представление о доступных возможностях, вот некоторые из наиболее часто используемых фильтров шаблонов:
-
default -
Если переменная ложна или пуста, использовать заданное значение по умолчанию. В противном случае использовать значение переменной. Например:
{{ value|default:"nothing" }}Если
valueне предоставлена или пуста, выше будет отображено «nothing». -
length -
Возвращает длину значения. Это работает как для строк, так и для списков. Например:
{{ value|length }}Если
valueравно['a', 'b', 'c', 'd'], вывод будет4. -
filesizeformat -
Форматирует значение как «человекочитаемый» размер файла (т. е.
'13 KB','4.1 MB','102 bytes'и т. д.). Например:{{ value|filesizeformat }}Если
valueравно 123456789, вывод будет117.7 MB.
Это лишь несколько примеров; см. справку по встроенным фильтрам для полного списка.
Вы также можете создавать свои собственные пользовательские фильтры шаблонов; см. Как создать пользовательские теги и фильтры шаблонов.
См. также
Интерфейс администрирования Django может содержать полную справку по всем тегам и фильтрам шаблонов, доступным для данного сайта. См. Документацию генератора Django admin.
Комментарии
Для того, чтобы прокомментировать часть строки в шаблоне, используйте синтаксис комментария: {# #}.
Например, этот шаблон отобразится как 'hello':
{# greeting #}hello
Комментарий может содержать любой шаблонный код, корректный или нет. Например:
{# {% if foo %}bar{% else %} #}
Этот синтаксис может использоваться только для однострочных комментариев (между разделителями {# и #} не допускаются новые строки). Если вам нужно прокомментировать многострочный фрагмент шаблона, см. тег comment.
Наследование шаблонов
Самая мощная (и, следовательно, самая сложная) часть движка шаблонов Django — наследование шаблонов. Наследование шаблонов позволяет создать базовый «каркас» шаблона, содержащий все общие элементы вашего сайта и определять блоки, которые дочерние шаблоны могут переопределить.
Давайте рассмотрим наследование шаблонов, начиная с примера:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>{% block title %}My amazing site{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
Этот шаблон, который мы назовём base.html, определяет HTML-каркас документа, который вы можете использовать для двухколоночной страницы. «Дочерние» шаблоны отвечают за заполнение пустых блоков контентом.
В этом примере тег block определяет три блока, которые могут быть заполнены дочерними шаблонами. Тег block просто сообщает движку шаблонов, что дочерний шаблон может переопределить эти части шаблона.
Дочерний шаблон может выглядеть так:
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
Ключевым здесь является тег extends. Он сообщает движку шаблонов, что этот шаблон «расширяет» другой шаблон. При оценке этого шаблона система шаблонов сначала ищет родительский шаблон — в данном случае «base.html».
В этот момент движок шаблонов заметит три тега block в base.html и заменит эти блоки содержимым дочернего шаблона. В зависимости от значения blog_entries, вывод может выглядеть так:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>My amazing blog</title>
</head>
<body>
<div id="sidebar">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>
<div id="content">
<h2>Entry one</h2>
<p>This is my first entry.</p>
<h2>Entry two</h2>
<p>This is my second entry.</p>
</div>
</body>
</html>
Обратите внимание, что поскольку дочерний шаблон не определил блок sidebar, используется значение из родительского шаблона. Содержимое внутри тега {% block %} в родительском шаблоне всегда используется в качестве резервного варианта.
Вы можете использовать любое количество уровней наследования. Один из распространенных способов использования наследования — трехступенчатый подход:
- Создайте шаблон
base.html, содержащий основной вид вашего сайта. - Создайте шаблон
base_SECTIONNAME.htmlдля каждого «раздела» вашего сайта. Например,base_news.html,base_sports.html. Эти шаблоны наследуются отbase.htmlи включают стили/дизайн, специфичные для раздела. - Создайте отдельные шаблоны для каждого типа страницы, например, новостной статьи или записи в блоге. Эти шаблоны наследуют соответствующий раздел шаблона.
Этот подход максимизирует повторное использование кода и помогает добавлять элементы в общие области контента, такие как навигация по разделу.
Вот несколько советов по работе с наследованием:
- Если вы используете
{% extends %}в шаблоне, он должен быть первым тегом шаблона в этом шаблоне. Иначе наследование шаблонов не сработает. - Чем больше тегов
{% block %}в ваших базовых шаблонах, тем лучше. Помните, что дочерние шаблоны не обязаны определять все родительские блоки, поэтому вы можете заполнить разумные значения в ряде блоков, а затем определить только те, которые вам нужны позже. Лучше иметь больше «ключей», чем меньше. - Если вы обнаружите, что дублируете контент в нескольких шаблонах, это, вероятно, означает, что вам следует перенести этот контент в
{% block %}в родительском шаблоне. - Если вам нужно получить содержимое блока из родительского шаблона, переменная
{{ block.super }}справится с этой задачей. Это полезно, если вы хотите добавить к содержимому родительского блока, а не полностью его переопределить. Данные, вставленные с помощью{{ block.super }}, не будут автоматически экранированы (см. следующий раздел «Автоматическое экранирование HTML»), поскольку они уже были экранированы, если это необходимо, в родительском шаблоне. - Используя то же имя шаблона, что и у наследуемого шаблона,
{% extends %}можно использовать для наследования шаблона одновременно с его переопределением. В сочетании с{{ block.super }}это может быть мощный способ внесения небольших изменений. См. Расширение переопределённого шаблона в разделе Переопределение шаблонов Справочного руководства для полного примера. -
Переменные, созданные за пределами
{% block %}с помощью синтаксиса тега шаблонаas, не могут использоваться внутри блока. Например, этот шаблон ничего не отображает:{% translate "Title" as title %} {% block content %}{{ title }}{% endblock %} -
Для большей читабельности вы можете необязательно присвоить имя вашему тегу
{% endblock %}. Например:{% block content %} ... {% endblock content %}В больших шаблонах этот метод помогает вам увидеть, какие теги
{% block %}закрываются. -
Теги
{% block %}оцениваются в первую очередь. Именно поэтому содержимое блока всегда переопределяется, независимо от истинности окружающих тегов. Например, этот шаблон всегда переопределит содержимое блокаtitle:{% if change_title %} {% block title %}Hello!{% endblock title %} {% endif %}
И наконец, обратите внимание, что вы не можете определить несколько тегов block с одинаковым именем в одном шаблоне. Это ограничение существует, потому что тег блока работает «в обоих» направлениях. То есть, тег блока не только предоставляет ячейку для заполнения, но также определяет содержимое, которое заполняет ячейку в родительском шаблоне. Если в шаблоне было два тега block с одинаковым именем, родительский шаблон не знал бы, какое из содержимых блоков использовать.
Автоматическая экранизация HTML
При генерации HTML из шаблонов всегда существует риск того, что переменная будет содержать символы, влияющие на итоговый HTML. Например, рассмотрим этот фрагмент шаблона:
Hello, {{ name }}
На первый взгляд, это безобидный способ отображения имени пользователя, но что произойдет, если пользователь введет свое имя так:
<script>alert('hello')</script>
С этим значением имени шаблон будет отображен как:
Hello, <script>alert('hello')</script>
…что означает, что браузер отобразит всплывающее окно JavaScript alert!
Точно так же, что произойдет, если имя будет содержать символ '<', например так?
<b>username
Это приведет к отображению шаблона следующим образом:
Hello, <b>username
…что, в свою очередь, приведет к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что данные, предоставленные пользователем, не должны слепо доверяться и вставляться непосредственно в ваши веб-страницы, так как злонамеренный пользователь может использовать такую брешь, чтобы совершить потенциально вредные действия. Этот тип атаки на безопасность называется межсайтовым скриптингом (XSS).
Чтобы избежать этой проблемы, у вас есть два варианта:
- Во-первых, вы можете убедиться, что каждая недоверенная переменная проходит через фильтр
escape(документирован ниже), который преобразует потенциально вредные символы HTML в безвредные. Это было решением по умолчанию в Django в первые несколько лет, но проблема в том, что это возлагает ответственность на вас, разработчика/автора шаблона, чтобы убедиться, что все экранируется. Легко забыть экранировать данные. - Во-вторых, вы можете воспользоваться автоматической экранизацией HTML Django. Остальная часть этого раздела описывает, как работает автоматическая экранизация.
По умолчанию в Django каждый шаблон автоматически экранирует вывод каждого тега переменной. В частности, экранируются следующие пять символов:
-
<преобразуется в< -
>преобразуется в> -
'(одинарная кавычка) преобразуется в' -
"(двойная кавычка) преобразуется в" -
&преобразуется в&
Еще раз подчеркнем, что это поведение включено по умолчанию. Если вы используете систему шаблонов Django, вы защищены.
Как отключить
Если вы не хотите, чтобы данные автоматически экранировались на уровне сайта, шаблона или переменной, вы можете отключить эту функцию несколькими способами.
Почему вы захотите это отключить? Потому что иногда переменные шаблонов содержат данные, которые вы хотите отобразить как обычный HTML, в этом случае вы не хотите, чтобы их содержимое экранировалось. Например, вы можете хранить фрагмент HTML в вашей базе данных и хотите вставить его непосредственно в шаблон. Или вы можете использовать систему шаблонов Django для создания текста, который не является HTML — например, сообщение электронной почты.
Для отдельных переменных
Чтобы отключить автоматическую экранизацию для отдельной переменной, используйте фильтр safe:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
Представьте, что safe — это сокращение от безопасный от дальнейшей экранизации или может быть безопасно интерпретирован как HTML. В этом примере, если data содержит '<b>', вывод будет:
This will be escaped: <b> This will not be escaped: <b>
Для блоков шаблона
Чтобы управлять автоматической экранизацией для шаблона, оберните шаблон (или конкретный раздел шаблона) тегом autoescape, как показано ниже:
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
Тег autoescape принимает либо on, либо off в качестве аргумента. Иногда вы можете принудительно включить автоматическую экранизацию, когда она отключена. Вот пример шаблона:
Auto-escaping is on by default. Hello {{ name }}
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}
Тег автоэкранирования передаёт своё действие шаблонам, расширяющим текущий шаблон, а также шаблонам, включённым с помощью тега include, точно так же, как и все теги блоков. Например:
base.html{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
child.html{% extends "base.html" %}
{% block title %}This & that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}
Поскольку автоэкранизация отключена в базовом шаблоне, она также будет отключена в дочернем шаблоне, что приведёт к следующему отображённому HTML при том, что переменная greeting содержит строку <b>Hello!</b>:
<h1>This & that</h1> <b>Hello!</b>
Примечания
В целом, авторы шаблонов не должны сильно беспокоиться об автоматической экранизации. Разработчики на стороне Python (люди, пишущие представления и пользовательские фильтры) должны думать о случаях, когда данные не должны экранироваться, и соответствующим образом отмечать данные, чтобы все работало в шаблоне.
Если вы создаёте шаблон, который может использоваться в ситуациях, когда вы не уверены, включена ли автоматическая экранизация, добавьте фильтр escape к любой переменной, которая нуждается в экранизации. Когда автоматическая экранизация включена, нет опасности того, что фильтр escape дважды экранирует данные — фильтр escape не влияет на переменные, экранированные автоматически.
Строковые литералы и автоматическая экранизация
Как мы упоминали ранее, аргументы фильтров могут быть строками:
{{ data|default:"This is a string literal." }}
Все строковые литералы вставляются без автоматической экранизации в шаблон — они ведут себя так, как будто они все были переданы через фильтр safe. Объяснение заключается в том, что автор шаблона контролирует то, что входит в строковый литерал, поэтому они могут убедиться, что текст правильно экранирован, когда шаблон написан.
Это означает, что вы должны написать:
{{ data|default:"3 < 2" }}
…а не:
{{ data|default:"3 < 2" }} {# Bad! Don't do this. #}
Это не влияет на то, что происходит с данными, поступающими из переменной. Содержимое переменной по-прежнему автоматически экранируется, если это необходимо, так как оно находится вне контроля автора шаблона.
Обращение к вызовам методов
Большинство вызовов методов, прикреплённых к объектам, также доступны в шаблонах. Это означает, что шаблоны имеют доступ к гораздо большему, чем просто атрибуты класса (такие как имена полей) и переменные, переданные из представлений. Например, Django ORM предоставляет синтаксис “entry_set” для поиска набора объектов, связанных по внешнему ключу. Таким образом, заданным моделью «комментарий» с отношением внешнего ключа к модели «задача» можно перебрать все комментарии, связанные с данной задачей, так:
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
Аналогично, QuerySets предоставляют метод count() для подсчёта количества содержащихся в них объектов. Таким образом, вы можете получить подсчёт всех комментариев, относящихся к текущей задаче, так:
{{ task.comment_set.all.count }}
Вы также можете получить доступ к методам, которые вы явно определили в своих собственных моделях:
models.pyclass Task(models.Model):
def foo(self):
return "bar"
template.html{{ task.foo }}
Поскольку Django намеренно ограничивает количество логических операций, доступных в языке шаблонов, невозможно передавать аргументы в вызовы методов, доступные в шаблонах. Данные должны вычисляться в представлениях, а затем передаваться в шаблоны для отображения.
Библиотеки пользовательских тегов и фильтров
Некоторые приложения предоставляют библиотеки пользовательских тегов и фильтров. Для доступа к ним в шаблоне убедитесь, что приложение находится в INSTALLED_APPS (для этого примера мы добавили бы 'django.contrib.humanize'), а затем используйте тег load в шаблоне:
{% load humanize %}
{{ 45000|intcomma }}
В приведенном выше примере тег load загружает библиотеку тегов humanize, которая затем делает доступным для использования фильтр intcomma. Если вы включили django.contrib.admindocs, вы можете найти список пользовательских библиотек в вашей установке в разделе документации администратора.
Тег load может принимать несколько имен библиотек, разделенных пробелами. Пример:
{% load humanize i18n %}
См. Как создать пользовательские теги и фильтры шаблонов для получения информации о написании собственных пользовательских библиотек шаблонов.
Пользовательские библиотеки и наследование шаблонов
При загрузке пользовательской библиотеки тегов или фильтров теги/фильтры доступны только для текущего шаблона — не для родительских или дочерних шаблонов по пути наследования шаблонов.
Например, если в шаблоне foo.html есть {% load humanize %}, дочерний шаблон (например, шаблон, который имеет {% extends "foo.html" %}) не будет иметь доступа к тегам и фильтрам шаблона humanize. Дочерний шаблон отвечает за свои собственные {% load humanize %}.
Это функция, обеспечивающая поддержку и разумность.
См. также
- Справочник по шаблонам
-
Покрывает встроенные теги, встроенные фильтры, использование альтернативного языка шаблонов и многое другое.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.2/ref/templates/language/