Язык шаблонов 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 %}в ваших базовых шаблонах — лучше. Помните, что дочерние шаблоны не обязаны определять все родительские блоки, поэтому вы можете заполнить разумные значения по умолчанию во многих блоках, а затем определить только те, которые вам нужны позже. Лучше иметь больше «устройств» (hooks), чем меньше. - Если вы обнаруживаете, что дублируете контент во многих шаблонах, это, вероятно, означает, что вам следует переместить этот контент в блок
{% block %}в родительском шаблоне. - Если вам нужно получить содержимое блока из родительского шаблона, переменная
{{ block.super }}сделает это. Это полезно, если вы хотите добавить к содержимому родительского блока, а не полностью его переопределить. Данные, вставленные с помощью{{ block.super }}, не будут автоматически экранированы (см. следующий раздел следующий раздел), так как они уже были экранированы, если это необходимо, в родительском шаблоне. - Используя то же имя шаблона, что и у наследуемого шаблона,
{% 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 — это сокращение от безопасный от дальнейшего экранирования или может быть безопасно интерпретирован как 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/4.1/ref/templates/language/