Язык шаблонов 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-шаблонов. Вы можете использовать язык шаблонов для любого текстового формата, такого как электронные письма, 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 }}, это может быть мощный способ внесения небольших изменений. См. Расширение переопределенного шаблона в разделе «Переопределение шаблонов» «Как-то» для полного примера. -
Переменные, созданные вне
{% 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!
Точно так же, что если имя содержало символ '<', например так?
<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 — это сокращение от safe from further escaping или может быть безопасно интерпретирован как 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” для поиска коллекции объектов, связанных по внешнему ключу. Таким образом, для модели под названием «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.0/ref/templates/language/