Язык шаблонов 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 }}, это может быть мощным способом внесения небольших изменений. См. Расширение переопределенного шаблона в разделе Переопределение шаблонов How-to для полного примера. -
Переменные, созданные вне
{% block %}с помощью синтаксиса тегаasне могут использоваться внутри блока. Например, этот шаблон ничего не отображает:{% translate "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 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 является сокращением от 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 %}
Тег auto-escaping передаёт своё действие шаблонам, которые расширяют текущий, а также шаблонам, включённым с помощью тега 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” для поиска коллекции объектов, связанных по внешнему ключу. Таким образом, учитывая модель под названием «комментарий» с отношением внешнего ключа к модели под названием «задача», вы можете перебрать все комментарии, прикреплённые к заданной задаче, так:
{% 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/3.1/ref/templates/language/