Язык шаблонов Django
В данном документе объясняется синтаксис языка Django для шаблонов. Если вы хотите получить более техническую информацию о работе и расширении этого механизма, обратитесь к Языку шаблонов Django: для программистов Python.
Язык шаблонов Django разработан для достижения баланса между мощностью и простотой. Он создан так, чтобы быть удобным для тех, кто работает с HTML. Если вы знакомы с другими текстовыми языками шаблонов, такими как Smarty или Jinja2, вам будет легко работать с шаблонами Django.
Философия
Если вы имеете опыт программирования или используете языки, которые смешивают код программы непосредственно в HTML, имейте в виду, что система шаблонов Django — не просто вложенный в HTML Python. Это сделано намеренно: система шаблонов предназначена для выражения представления, а не логики программы.
Система шаблонов 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.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»), так как они уже были экранированы, если это было необходимо, в родительском шаблоне. -
Переменные, созданные вне
{% 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
…что, в свою очередь, приведет к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что данные, отправленные пользователем, не должны восприниматься без проверки и вставляться непосредственно в ваши веб-страницы, потому что злонамеренный пользователь может использовать такую уязвимость для совершения потенциально вредных действий. Этот тип атаки на безопасность называется атакой межсайтового скриптинга (XSS).
Чтобы избежать этой проблемы, у вас есть два варианта:
- Во-первых, вы можете убедиться, что каждая недоверенная переменная проходит через фильтр
escape(документирован ниже), который преобразует потенциально вредные символы HTML в безопасные. Это было стандартным решением в Django в первые несколько лет, но проблема в том, что ответственность за экранирование ложится на вас, разработчика / автора шаблона, и вы можете забыть об экранировании данных. - Во-вторых, вы можете воспользоваться автоматическим экранированием HTML Django. Остальная часть этого раздела описывает, как работает автоматическое экранирование.
По умолчанию в Django каждый шаблон автоматически экранирует вывод каждого тега переменной. Конкретно, экранируются пять символов:
-
<преобразуется в< -
>преобразуется в> -
'(одинарная кавычка) преобразуется в' -
"(двойная кавычка) преобразуется в" -
&преобразуется в&
Еще раз подчеркнем, что это поведение включено по умолчанию. Если вы используете систему шаблонов Django, вы защищены.
Как отключить
Если вы не хотите, чтобы данные были автоматически экранированы на уровне сайта, шаблона или переменной, вы можете отключить эту функцию несколькими способами.
Почему вам может понадобиться отключить эту функцию? Иногда переменные шаблонов содержат данные, которые вы хотите отобразить как исходный HTML, в этом случае вы не хотите, чтобы их содержимое экранировалось. Например, вы можете хранить кусок HTML в базе данных и хотите встроить его непосредственно в свой шаблон. Или вы можете использовать систему шаблонов Django для создания текста, который не является HTML, например, сообщения электронной почты.
Для отдельных переменных
Чтобы отключить автоматическое экранирование для отдельной переменной, используйте фильтр safe:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
Представьте, что safe — это сокращение от safe from further escaping или можно безопасно интерпретировать как HTML. В этом примере, если data содержит '<b>', вывод будет:
This will be escaped: <b> This will not be escaped: <b>
Для блоков шаблонов
Чтобы контролировать автоматическое экранирование для шаблона, заключите шаблон (или только определенный раздел шаблона) в тег autoescape, как показано ниже:
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
Тег autoescape принимает on или off в качестве аргумента. Иногда вам может потребоваться принудительно включить автоматическое экранирование, когда оно было отключено. Вот пример шаблона:
Auto-escaping is on by default. Hello {{ name }}
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}
Тег автоматического экранирования передает свое влияние на шаблоны, которые расширяют текущий шаблон, а также на шаблоны, включенные с помощью тега include, точно так же, как и все теги блоков. Например:
{% 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/2.0/ref/templates/language/