Язык шаблонов Django¶
Этот раздел описывает синтаксис языка шаблонов Django. Если вы ищете технически подробности как он работает и как расширять его, смотрите Язык шаблонов Django: для Python программистов.
Язык шаблонов Django представляет баланс между возможностями и простотой. Он создавался, чтобы быть удобным для пользователей HTML. Если у вас есть опыт работы с другими языками текстовых шаблонов, таких как Smarty или Jinja2, вы должны себя чувствовать как дома с шаблонами Django.
Философия
Если у вас есть опыт программирования или вы использовали PHP, который позволяет интегрировать программный код прямо 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.
Переменные¶
Переменные выглядят следующим образом: {{ переменная }}. Когда механизм шаблонов встречает переменную, он оценивает эту переменную и заменяет ее результатом. Имена переменных состоят из любой комбинации буквенно-цифровых символов и подчеркивания («_»), но не могут начинаться с подчеркивания и не могут быть числами. Точка («.»``) также появляется в разделах переменных, хотя она имеет особое значение, как указано ниже. Важно отметить, что в именах переменных нельзя использовать пробелы или знаки препинания.
Используйте точку (.) для доступа к атрибутам переменной.
За кулисами
Технически, когда шаблон встречает точку, он пытается получить значения в таком порядке:
Ключ словаря
Атрибут или метод
Индекс списка
Если найден вызываемый объект(функция или метод), он будет вызван без аргументов. Результат будет использоваться шаблоном как значение.
Этот порядок поиска может привести к неожиданному поведению объектов, которые переопределяют поиск по словарю. Например, рассмотрим следующий фрагмент кода, который пытается перебрать 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», а не переменная с названием «bar».
Атрибуты переменных, начинающиеся с подчеркивания, могут быть недоступны, поскольку они обычно считаются частными.
Фильтры¶
Вы можете изменить значение переменной используя фильтры.
Фильтры выглядят таким образом: {{ name|lower }}. Это выведет значение переменной {{ name }} после применения фильтра lower к нему, который преобразует значение в нижний регистр. Используйте символ (|) для применения фильтра.
Можно использовать «цепочку» фильтров. Вывод одного фильтра используется для другого. {{ text|escape|linebreaks }} обычно применяется для экранирования текста, и замены переноса строки тегами <p>.
Некоторые фильтры принимают аргументы. Аргумент фильтра выглядит таким образом: {{ bio|truncatewords:30 }}. Этот код отобразит первые 30 слов переменной bio.
Аргументы фильтров, которые содержат пробелы, должны быть заключены в кавычки. Например, чтобы объединить список пробелом и запятой, используйте {{ list|join:", " }}.
Django предоставляет около шестидесяти встроенных фильтров. Вы можете прочитать о них в разделе о встроенных фильтрах. Чтобы дать вам представление о возможностях, вот некоторые из наиболее часто используемых фильтров:
defaultЕсли переменная имеет значение false или пуста, используйте значение по умолчанию. В противном случае используйте значение переменной. Например:
{{ value|default:"nothing" }}
Если
valueравно""(пустая строка), будет выведеноnothing.lengthВозвращает длину значения. Это работает как для строк, так и для списков. Например:
{{ value|length }}
Если
valueравно['a', 'b', 'c', 'd'], выведет4.- filesizeformat
Форматирует значение как «удобочитаемый» размер файла (т.е.
'13 КБ'`, ``'4,1 МБ','102 байта'и т. д.). Например:{{ value|filesizeformat }}
Если
valueравно 123456789, выведет117.7 MB.
Опять же, это всего лишь несколько примеров; полный список смотрите раздел о встроенных фильтрах.
Вы можете создать собственный фильтр; смотрите Как создать пользовательские теги и фильтры шаблонов.
См.также
Интерфейс администратора Django может предоставить полный список доступных тегов и фильтров. Смотрите Генератор документации администратора Django.
Комментарии¶
Чтобы закомментировать строку в шаблоне, используйте синтаксис комментариев: {# #}.
Например, этот шаблон будет отображаться как «привет»:
{# greeting #}hello
Комментарий может содержать любой код шаблона, недействительный или нет. Например:
{# {% if foo %}bar{% else %} #}
Этот синтаксис может быть использован только для однострочных комментариев (нельзя использовать перенос строки между {# и #}). Если вам нужно закомментировать несколько строк, используйте тег comment.
Наследование шаблонов¶
Самая могущественная – и, следовательно, самая сложная – часть механизма шаблонов Django – это наследование шаблонов. Наследование шаблонов позволяет создать вам шаблон-«скелет», который содержит базовые элементы вашего сайта и определяет блоки, которые могут быть переопределены дочерними шаблонами.
Давайте рассмотрим наследование шаблонов, начав с примера:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>{% block title %}My amazing site{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
Этот шаблон, который мы будем называть base.html, определяет HTML структуру документа, которую вы можете использовать для двух-колоночной страницы. Задача «дочернего» шаблона заполнить пустые блоки содержимым.
В этом примере, тег block определяет три блока, которые может переопределить дочерний шаблон. Все что делает тег block – указывает механизму шаблонов, какая часть шаблона может быть переопределена в дочернем шаблоне.
Дочерний шаблон может выглядеть так:
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
Ключевым здесь есть тег extends. Он говорит механизму шаблонов, что этот шаблон «наследует» другой шаблон. Когда механизм шаблонов выполняет этот шаблон, первым делом находится родительский шаблон – в этом примере «base.html».
В этот момент механизм шаблонов заметит три тега block в base.html и заменит эти блоки содержимым дочернего шаблона. В зависимости от значения blog_entries вывод может выглядеть так:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>My amazing blog</title>
</head>
<body>
<div id="sidebar">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>
<div id="content">
<h2>Entry one</h2>
<p>This is my first entry.</p>
<h2>Entry two</h2>
<p>This is my second entry.</p>
</div>
</body>
</html>
Так как дочерний шаблон не определяет блок sidebar, будет использовано значение из родительского шаблона. Содержимое тега {% block %} родительского шаблона всегда используется как значение по умолчанию.
Вы можете использовать столько уровней наследование, сколько вам нужно. Один из распространенных способов использовать наследование – это трехуровневый подход:
Создать шаблон
base.html, который отображает основной вид вашего сайта.Создать шаблон
base_SECTIONNAME.htmlдля каждого «раздела» вашего сайта. Например,base_news.html,base_sports.html. Все эти шаблоны наследуютbase.htmlи включают стили и дизайн специфические для конкретного раздела.Создание шаблона для каждого типа страницы, такие как новость или запись в блоге. Эти шаблоны наследуют соответствующий шаблон раздела.
Такой подход позволяет максимально использовать существующий код и легко добавлять элементы, такие как элементы навигации специфические для каждого раздела, в общие блоки шаблона.
Вот несколько советов по работе с наследованием:
Если вы используете
{% extends %}, он должен быть первым тегом в шаблоне. Иначе наследование не будет работать.Чем больше тегов
{% block %}в вашем шаблоне, тем лучше. Помните, дочерний шаблон не обязан определять все блоки родительского, вы можете указать значение по умолчанию для всех блоков, а затем определить в дочернем шаблоне только те, которые необходимы. Лучше иметь больше «hooks», чем меньше «hooks».Если вы дублируете содержимое в нескольких шаблонах, возможно вы должны перенести его в тег
{% block %}родительского шаблона.Если вам необходимо содержимое блока родительского шаблона, используйте переменную
{{ block.super }}. Эта полезно, если вам необходимо дополнить содержимое родительского блока, а не полностью переопределить его. Содержимое{{ block.super }}не будет автоматически экранировано (смотрите раздел ниже), так как оно уже было экранировано, при необходимости, в родительском шаблоне.Используя то же имя шаблона, от которого вы наследуете,
{% расширяет %}можно использовать для наследования шаблона одновременно с его переопределением. В сочетании с{{block.super }}это может быть мощным способом внесения небольших изменений. Полный пример смотрите в разделе Расширение переопределенного шаблона в разделе Переопределение шаблонов.Переменные, созданные вне блока
{% %}с использованием синтаксиса тега шаблонаas, не могут использоваться внутри блока. Например, этот шаблон ничего не отображает:{% translate "Title" as title %} {% block content %}{{ title }}{% endblock %}
Для большей читабельности вы можете при желании присвоить имя вашему тегу
{% endblock %}. Например:{% block content %} ... {% endblock content %}
В больших шаблонах такой подход поможет вам увидеть какой тег
{% block %}был закрыт.Теги
{%block %}оцениваются в первую очередь. Вот почему содержимое блока всегда переопределяется, независимо от правдивости окружающих тегов. Например, этот шаблон всегда переопределяет содержимое блокаtitle:{% if change_title %} {% block title %}Hello!{% endblock title %} {% endif %}
Вы не можете определить несколько тегов block с одним названием в одном шаблоне. Такое ограничение существует потому, что тег block работает в «оба» направления. block не просто предоставляет «полость» в шаблоне – он определяет содержимое, которое заполняет «полость» в родительском шаблоне. Если бы было несколько тегов block с одним названием, родительский шаблон не знал содержимое какого блока использовать.
Автоматическое экранирование HTML¶
При создании HTML из шаблонов всегда существует риск того, что переменная будет содержать символы, которые повлияют на результирующий HTML. Например, рассмотрим этот фрагмент шаблона:
Hello, {{ name }}
На первый взгляд это кажется безобидным способом отображения имени пользователя, но представьте, что произойдет, если пользователь введет свое имя следующим образом:
<script>alert('hello')</script>
С этим значением имени шаблон будет отображаться как:
Hello, <script>alert('hello')</script>
…что приведет к отображению alert-окна JavaScript!
Аналогично, что если имя содержит символ '<'?
<b>username
В результате получится такой шаблон:
Hello, <b>username
…что, в свою очередь, приведет к тому, что остальная часть веб-страницы будет выделена жирным шрифтом!
Очевидно, что не следует слепо доверять данным, предоставленным пользователем, и вставлять их непосредственно на ваши веб-страницы, поскольку злонамеренный пользователь может использовать такую дыру для совершения потенциально плохих поступков. Этот тип уязвимости безопасности называется атакой «Межсайтовый скриптинг» (XSS).
Чтобы избежать этой проблемы, у вас есть два варианта:
Первый, вы можете применять ко всем сомнительным переменным фильтр
escape(описанный далее), который преобразует потенциально опасные HTML символы в безопасные. Такое решение было принятым в первых версиях Django, но проблема в том, что оно возлагает бремя ответственности за безопасность на вас, разработчика / автора шаблона. Легко забыть экранировать переменную.Второй, вы можете позволить Django автоматически экранировать HTML. Оставшаяся часть этого раздела описывает, как автоматическое экранирование работает.
По-умолчанию в Django, каждый шаблон экранирует все переменные. В частности выполняются такие замены:
<заменяется на<>заменяется на>'(одинарная кавычка) заменяется на'"(двойная кавычка) заменяется на"&заменяется на&
Опять же, мы подчеркиваем, что такое поведение используется по умолчанию. Если вы используете систему шаблонов Django, вы в безопасности.
Как это отключить¶
Если вы не хотите, чтобы данные автоматически экранировались, на уровне сайта, шаблона или одной переменной, вы можете отключить это несколькими способами.
Зачем вам отключить экранирование? Потому что в некоторых ситуациях, вы намеренно добавляете HTML в переменную, и хотите, чтобы он выводился без экранирования. Например, вы можете хранить HTML в базе данных и хотите непосредственно вставить его в содержимое страницы. Или шаблоны Django используются для создания текста, который не является HTML – например email.
Для отдельных переменных¶
Чтобы отключить автоматическое экранирование для отдельной переменной, используйте фильтр 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 %}
Тег autoescape распространяет свой эффект на шаблоны, которые наследуют текущий, и на включенные тегом include шаблоны, как и другие блочные теги. Например:
base.html¶{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
child.html¶{% extends "base.html" %}
{% block title %}This & that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}
Поскольку автоматическое экранирование отключено в базовом шаблоне, оно также будет отключено и в дочернем шаблоне, в результате чего будет отображен следующий HTML-код, если переменная greeting содержит строку <b>Hello!</b>:
<h1>This & that</h1>
<b>Hello!</b>
Заметки¶
Обычно, авторы шаблонов не должны волноваться про авто-экранирование. Разработчики на стороне Python (те, кто создает представления и фильтры) должны определять в каких случаях данные не должны экранироваться и помечать их соответствующим образом, так что все будет просто работать в шаблонах.
Если вы создаете шаблон, который может использовать как с включенным авто-экранированием так и без него, добавляйте фильтр escape для каждой переменной, которую нужно экранировать. При включенном авто-экранировании фильтр escape не выполнит замену символов повторно.
Строки и автоматическое экранирование¶
Как мы упоминали ранее, аргументы фильтра могут быть строками:
{{ 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 }}
Вы также можете получить доступ к методам, которые вы явно определили в своих собственных моделях:
models.py¶class Task(models.Model):
def foo(self):
return "bar"
шаблон.html¶{{ task.foo }}
Так как Django намеренно ограничивает определение логики проекта в шаблоне, передавать аргументы при вызове метода в шаблоне нельзя. Данные должны вычисляться в представлении и передаваться в шаблон для отображения.
Собственные библиотеки тегов и фильтров¶
Некоторые приложения предоставляют собственные библиотеки тегов и фильтров. Чтобы получить к ним доступ в шаблоне, убедитесь, что приложение находится в состоянии INSTALLED_APPS (для этого примера мы бы добавили 'django.contrib.humanize'), а затем используйте тег load в шаблоне:
{% load humanize %}
{{ 45000|intcomma }}
В это примере, тег load загружает библиотеку humanize, которая предоставляет тег intcomma. Если вы активировали django.contrib.admindocs, в разделе документации вашего интерфейса администратора вы можете найти список всех установленных библиотек.
Тег load может содержать несколько имен библиотек, разделенных пробелами. Пример:
{% load humanize i18n %}
Смотрите раздел Как создать пользовательские теги и фильтры шаблонов, чтобы узнать как создавать собственные библиотеки для шаблонов.
Собственные библиотеки и наследование шаблонов¶
При загрузке библиотеки, теги и фильтры, которые она содержит, будут доступны только в текущем шаблоне – не в родительском или дочернем шаблонах.
Например, если шаблон foo.html содержит {% load humanize %}, дочерний шаблон (например, содержащий``{% extends «foo.html» %}``) не сможет использовать теги и фильтры из этой библиотеки. Дочерний шаблон должен самостоятельно загрузить библиотеку, используя {% load humanize %}.
Так сделано ради «maintainability» и «sanity».
См.также
- Описание системы шаблонов
Охватывает встроенные теги, встроенные фильтры, использование альтернативного языка шаблонов и многое другое.