Язык шаблонов 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-шаблона (например, TAL Zope)? Мы хотели, чтобы язык шаблонов Django мог использоваться не только для шаблонов XML/HTML. В World Online мы используем его для писем, JavaScript и CSV. Вы можете использовать язык шаблонов для любого текстового формата.
Ах да, и ещё кое-что: заставлять людей редактировать XML — это садизм!
Переменные
Переменные выглядят так: {{ 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.
Теги
Некоторые теги требуют начального и конечного тегов (т. е. {% tag %} ... tag contents
... {% endtag %}).
Django поставляется с около двумя десятками встроенных тегов шаблонов. Вы можете прочитать обо всех них в справочнике по встроенным тегам. Чтобы дать вам представление о доступных возможностях, вот некоторые из наиболее часто используемых тегов:
-
for -
Перебирает каждый элемент в массиве. Например, для отображения списка спортсменов, предоставленных в
athlete_list:<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul> -
if, elif, and else -
Оценивает переменную, и если эта переменная «истина», содержимое блока отображается:
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% elif athlete_in_locker_room_list %} Athletes should be out of the locker room soon! {% else %} No athletes. {% endif %}В приведенном выше примере, если
athlete_listне пусто, количество спортсменов будет отображено переменной{{ athlete_list|length }}. В противном случае, еслиathlete_in_locker_room_listне пусто, будет отображено сообщение «Спортсмены должны выйти…». Если оба списка пусты, будет отображено «Нет спортсменов».Вы также можете использовать фильтры и различные операторы в теге
if:{% if athlete_list|length > 1 %} Team: {% for athlete in athlete_list %} ... {% endfor %} {% else %} Athlete: {{ athlete_list.0.name }} {% endif %}Хотя приведенный выше пример работает, имейте в виду, что большинство фильтров шаблонов возвращают строки, поэтому математические сравнения с использованием фильтров обычно не работают так, как ожидается.
length— исключение. -
block andextends - Устанавливает наследование шаблонов (см. ниже) — мощный способ сократить «шаблонные фрагменты» в шаблонах.
Опять же, выше приведен только выбор из всего списка; см. справочник по встроенным тегам для полного списка.
Вы также можете создавать собственные пользовательские теги шаблонов; см. Пользовательские теги и фильтры шаблонов.
См. также
Интерфейс администрирования 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»), поскольку они уже были экранированы, если это необходимо, в родительском шаблоне. -
Переменные, созданные вне тега
{% block %}с использованием синтаксиса тегаasне могут быть использованы внутри блока. Например, этот шаблон ничего не отображает:{% trans "Title" as title %} {% block content %}{{ title }}{% endblock %} -
Для лучшей читаемости вы можете необязательно присвоить имя тегу
{% endblock %}. Например:{% block content %} ... {% endblock content %}В больших шаблонах это помогает увидеть, какие теги
{% block %}закрываются.
Наконец, обратите внимание, что вы не можете определить несколько тегов block с одним и тем же именем в одном и том же шаблоне. Это ограничение существует, потому что тег блока работает «в обоих» направлениях. То есть тег блока не просто предоставляет отверстие для заполнения — он также определяет содержимое, заполняющее отверстие в родительском шаблоне. Если в шаблоне были два тега block с одинаковым именем, родительский шаблон не знал бы, какое из содержимых блоков использовать.
Автоматическое экранирование HTML
При генерации HTML из шаблонов всегда существует риск, что переменная будет содержать символы, влияющие на результирующий HTML. Например, рассмотрим этот фрагмент шаблона:
Hello, {{ name }}
На первый взгляд, это безобидный способ отображения имени пользователя, но представьте, что произойдёт, если пользователь введёт своё имя так:
<script>alert('hello')</script>
С этим значением имени шаблон будет отображен как:
Hello, <script>alert('hello')</script>
…что означает, что браузер отобразит окно с предупреждением JavaScript!
Аналогично, что если имя содержало символ '<', как в этом примере?
<b>username
Это приведёт к отображению шаблона так:
Hello, <b>username
…что в свою очередь приведёт к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что данные, отправляемые пользователем, не должны слепо доверяться и вставляться непосредственно в ваши веб-страницы, так как злонамеренный пользователь может использовать такую брешь для потенциально вредных действий. Такой вид уязвимости в системе безопасности называется атакой типа Cross Site Scripting (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, точно так же, как все теги блоков. Например:
{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
{% 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” для поиска коллекции объектов, связанных по внешнему ключу. Таким образом, учитывая модель под названием «comment» с отношением внешнего ключа к модели под названием «task», вы можете перебрать все комментарии, прикреплённые к заданной задаче, следующим образом:
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
Аналогично, QuerySets предоставляют метод count() для подсчёта количества объектов, которые они содержат. Таким образом, вы можете получить количество всех комментариев, связанных с текущей задачей, с помощью:
{{ task.comment_set.all.count }}
И, конечно же, вы можете легко получить доступ к методам, которые вы явно определили в собственных моделях:
class Task(models.Model):
def foo(self):
return "bar"
{{ 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/2.2/ref/templates/language/