Язык шаблонов 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 администрирования.
Теги
Некоторые теги требуют начального и конечного тега (например, {% 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 администрирования.
Комментарии
Чтобы прокомментировать часть строки в шаблоне, используйте синтаксис комментария: {# #}.
Например, этот шаблон отобразится как '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 с одинаковым именем в одном шаблоне. Это ограничение существует, потому что тег блока работает «в обоих направлениях». То есть тег блока не только предоставляет место для заполнения, но также определяет контент, который заполняет эту область в *родительском* шаблоне. Если в шаблоне были два тега block с одинаковым именем, родительский шаблон не знал бы, какое содержимое из блоков использовать.
Автоматическое экранирование HTML
При генерации HTML из шаблонов всегда есть риск, что переменная будет содержать символы, влияющие на итоговый HTML. Например, рассмотрим этот фрагмент шаблона:
Hello, {{ name }}
На первый взгляд, это безобидный способ отображения имени пользователя, но представьте, что произойдет, если пользователь введёт своё имя так:
<script>alert('hello')</script>
С этим значением имени шаблон будет отображен как:
Hello, <script>alert('hello')</script>
…что означает, что браузер выведет окно с диалогом JavaScript!
Аналогично, что произойдёт, если имя будет содержать символ '<' , как в этом примере?
<b>username
Это приведёт к отображению шаблона так:
Hello, <b>username
…что, в свою очередь, приведёт к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что данные, предоставленные пользователем, не должны слепо доверяться и вставляться непосредственно в ваши веб-страницы, потому что злоумышленный пользователь может использовать такую брешь для потенциально вредных действий. Такой тип атаки на безопасность называется атакой типа «Межсайтовый скриптинг» (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 %}
Тег 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” для поиска набора объектов, связанных по внешнему ключу. Поэтому, учитывая модель с именем «comment» с внешним ключом к модели с именем «task», вы можете перебирать все комментарии, прикреплённые к заданной задаче, следующим образом:
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
Аналогично, QuerySets предоставляют метод count() для подсчёта количества содержащихся в них объектов. Таким образом, вы можете получить количество всех комментариев, связанных с текущей задачей, с помощью:
{{ task.comment_set.all.count }}
Вы также можете получить доступ к методам, которые вы явно определили в собственных моделях:
class Task(models.Model):
def foo(self):
return "bar"
{{ task.foo }}
Поскольку Django намеренно ограничивает количество логических операций, доступных в языке шаблонов, невозможно передавать аргументы в вызовы методов, используемые внутри шаблонов. Данные должны рассчитываться в представлениях, а затем передаваться в шаблоны для отображения.
Загрузка пользовательских библиотек шаблонов
Некоторые приложения предоставляют пользовательские библиотеки тегов и фильтров. Чтобы получить к ним доступ в шаблоне, убедитесь, что приложение находится в INSTALLED_APPS (мы добавим 'django.contrib.humanize' для этого примера), а затем используйте тег load в шаблоне:
{% load humanize %}
{{ 45000|intcomma }}
В приведённом выше примере тег load загружает библиотеку тегов humanize, которая затем делает фильтр intcomma доступным для использования. Если вы включили django.contrib.admindocs, вы можете просмотреть список пользовательских библиотек в вашем административном интерфейсе.
Тег load может принимать несколько имён библиотек, разделённых пробелами. Пример:
{% load humanize i18n %}
См. Пользовательские теги и фильтры шаблонов для получения информации о написании собственных пользовательских библиотек шаблонов.
Пользовательские библиотеки и наследование шаблонов
Когда вы загружаете пользовательскую библиотеку тегов или фильтров, теги/фильтры доступны только для текущего шаблона — не для родительских или дочерних шаблонов по пути наследования шаблонов.
Например, если шаблон foo.html имеет {% load humanize %}, дочерний шаблон (например, тот, у которого есть {% extends "foo.html" %}) не будет иметь доступа к тегам и фильтрам шаблона humanize. Дочерний шаблон отвечает за свою собственную {% load humanize %}.
Это реализовано для обеспечения поддерживаемости и здравого смысла.
См. также
- Справочник по шаблонам
- Охватывает встроенные теги, встроенные фильтры, использование альтернативного языка шаблонов и многое другое.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.2/ref/templates/language/