Язык шаблонов 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.
Теги
Некоторые теги требуют начального и конечного тега (например, {% 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»), поскольку они уже были экранированы, если это необходимо, в родительском шаблоне. - Используя то же имя шаблона, что и у наследоваемого шаблона,
{% extends %}можно использовать для наследования шаблона одновременно с его переопределением. В сочетании с{{ block.super }}, это может быть мощный способ внесения небольших изменений. См. Расширение переопределенного шаблона в разделе Overriding templates How-to для полного примера. -
Переменные, созданные за пределами
{% 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
…что, в свою очередь, приведет к тому, что остальная часть веб-страницы будет отображаться жирным шрифтом!
Очевидно, что данные, введенные пользователем, не должны слепо доверяться и вставляться непосредственно в ваши веб-страницы, потому что злонамеренный пользователь может использовать такую брешь, чтобы совершить потенциально вредные действия. Этот тип атаки на безопасность называется атакой 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, как и все теги блоков. Например:
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 (люди, пишущие представления и пользовательские фильтры) должны думать о случаях, когда данные не должны быть экранированы, и должным образом маркировать данные, чтобы все работало в шаблоне.
END_OF_DOCUMENT_MARKERЕсли вы создаёте шаблон, который может использоваться в ситуациях, когда вы не уверены, включена ли автоматическая экранизация, добавьте фильтр 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 }}
Вы также можете получить доступ к методам, которые вы явно определили в своих собственных моделях:
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.1/ref/templates/language/