Язык шаблонов 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. В World Online мы используем его для писем, JavaScript и CSV. Вы можете использовать язык шаблонов для любого текстового формата.
Ах, и еще кое-что: заставлять людей редактировать XML — это садизм!
Переменные
Переменные выглядят так: {{ variable }}. Когда движок шаблонов встречает переменную, он оценивает эту переменную и заменяет её результатом. Имена переменных состоят из любой комбинации буквенно-цифровых символов и знака подчеркивания ("_"). Точка ("." ) также появляется в разделах переменных, хотя это имеет специальное значение, как указано ниже. Важно, что вы не можете использовать пробелы или знаки препинания в именах переменных.
Используйте точку (. ), чтобы получить доступ к атрибутам переменной.
За кулисами
Технически, когда система шаблонов встречает точку, она пытается выполнить следующие запросы в этом порядке:
- Поиск в словаре
- Поиск атрибута или метода
- Поиск по числовому индексу
Если полученное значение вызываемое, оно вызывается без аргументов. Результат вызова становится значением шаблона.
Этот порядок поиска может привести к некоторым неожиданным результатам с объектами, которые переопределяют поиск в словаре. Например, рассмотрим следующий фрагмент кода, который пытается пройти по циклу collections.defaultdict:
{% for k, v in defaultdict.iteritems %}
Do something with k and v here...
{% endfor %}
Поскольку поиск в словаре происходит первым, это поведение срабатывает и предоставляет значение по умолчанию вместо использования предполагаемого .iteritems() метода. В этом случае подумайте о преобразовании в словарь сначала.
В приведенном выше примере {{ 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»), так как они уже были экранированы, если это необходимо, в родительском шаблоне. -
Переменные, созданные вне
{% block %}с помощью синтаксиса тега шаблонаas, не могут использоваться внутри блока. Например, этот шаблон ничего не отображает:{% trans "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
…что, в свою очередь, приведет к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что данные, введённые пользователем, не должны доверять бездумно и вставляться непосредственно в ваши веб-страницы, потому что злоумышленник может использовать такую брешь, чтобы нанести потенциальный вред. Этот тип уязвимости безопасности называется атакой 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 или can be safely interpreted as 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, точно так же, как все теги блоков. Например:
{% 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/1.11/ref/templates/language/