Язык шаблонов Django

About this document

Этот раздел описывает синтаксис языка шаблонов Django. Если вы ищете технически подробности как он работает и как расширять его, смотрите The Django template language: For Python programmers.

Язык шаблонов Django представляет баланс между возможностями и простотой. Он создавался, чтобы быть удобным для пользователей HTML. Если у вас есть опыт работы с другими языками текстовых шаблонов, таких как Smarty или CheetahTemplate, вы должны себя чувствовать как дома с шаблонами Django.

Philosophy

Если у вас есть опыт программирования или вы использовали 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 %}

Philosophy

Зачем использовать текстовый шаблон, а не на основе XML (как TAL в Zope)? Мы хотели, чтобы шаблоны Django были больше, чем просто XML/HTML шаблоны. На World Online, мы используем их для email, JavaScript и CSV. Вы можете использовать шаблоны для любого текстового формата.

Ах да, еще одно: заставлять людей редактировать XML – это садизм!

Переменные

Переменные выглядят таким образом: {{ variable }}. Когда шаблон встречает переменную, он вычисляет ее и заменяет результатом. Названия переменных могут состоять из букв, цифр и нижнего подчеркивания("_"). Точку (".") также можно использовать, но она имеет особый смысл описанный ниже. Важно, нельзя использовать пробелы и знаки пунктуации в названии переменных.

Используйте точку (.) для доступа к атрибутам переменной.

Behind the scenes

Технически, когда шаблон встречает точку, он пытается получить значения в таком порядке:

  • Ключ словаря

  • Атрибут

  • Вызов метода

  • Индекс списка

Это может приводить к неожиданным результатам с объектами, которые переопределяют поиск по словарю. Например, рассмотрим следующий код, который пытается выполнить цикл по collections.defaultdict:

{% for k, v in defaultdict.iteritems %}
    Do something with k and v here...
{% endfor %}

Так как поиск словаре происходит вперёд, такое поведение предоставляет значение по умолчанию вместо использования метода .iteritems(). В данном случае, сначала рассмотрим преобразование в словарь.

В примере выше, {{ section.title }} будет заменен на атрибут title объекта section.

Если переменная не найдена, шаблон вставит значение из настройки TEMPLATE_STRING_IF_INVALID, которая равна '' (пустой строке) по-умолчанию.

Следует отметить, что “bar” в шаблонном выражении выглядит как {{ foo.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.

striptags

Удаляет все [X]HTML теги. Например:

{{ value|striptags }}

Если value равно "<b>Joel</b> <button>is</button> a <span>slug</span>", выведет "Joel is a slug".

Опять же, это всего лишь несколько примеров; полный список смотрите раздел о встроенных фильтрах.

Вы можете создать собственный фильтр; смотрите Собственные шаблонные теги и фильтры.

См.также

Интерфейс администратора Django может предоставить полный список доступных тегов и фильтров. Смотрите The Django admin documentation generator.

Теги

Теги выглядят таким образом: {% tag %}. Теги сложнее чем переменные: одни создают текст для вывода, влияют на выполнение используя условия и циклы, другие загружают дополнительную информацию в шаблоны, чтобы использовать ее далее через переменные.

Некоторые теги требуют открывающий и закрывающий теги (например, {% tag %} ... содержимое тега ... {% endtag %}).

Django содержит около двадцати встроенных тегов. Вы можете прочитать все о них в разделе о встроенных тегах. Чтобы дать вам представление о возможностях, вот некоторые из наиболее часто используемых тегов:

for

Цикл по каждому элементу массива. Например, выведем список спортсменов из athlete_list:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if и else

Вычисляет переменную и если она равна “true”, выводит содержимое блока:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% else %}
    No athletes.
{% endif %}

В примере выше, если athlete_list не пустой, будет отображено количество спортсменов {{ athlete_list|length }}.

Вы можете использовать фильтры и операторы в теге if tag:

{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}

Несмотря на работоспособность вышеупомянутого примера, помните, что большинство шаблонных фильтров возвращает строки, таким образом, математическое сравнение результатов фильтров в общем случае будет работать не так, как вы можете ожидать. Хотя : tfilter:length является исключением.

block и extends

Определяет наследование шаблонов (смотрите ниже), эффективный способ использовать шаблоны.

Опять же, это только несколько тегов; полный список смотрите в разделе о встроенных тегах.

Вы можете создать собственный тег; смотрите Собственные шаблонные теги и фильтры.

См.также

Интерфейс администратора Django может предоставить полный список доступных тегов и фильтров. Смотрите The Django admin documentation generator.

Комментарии

Чтобы закомментировать строку в шаблоне, используйте синтаксис комментариев: {# #}.

Например, этот шаблон выведет '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 %} в вашем шаблоне, тем лучше. Помните, дочерний шаблон не обязан определять все блоки родительского, вы можете указать значение по умолчанию для всех блоков, а затем определить в дочернем шаблоне только те, которые необходимы. Лучше иметь больше “hooks”, чем меньше “hooks”.

  • Если вы дублируете содержимое в нескольких шаблонах, возможно вы должны перенести его в тег {% block %} родительского шаблона.

  • Если вам необходимо содержимое блока родительского шаблона, используйте переменную {{ block.super }}. Эта полезно, если вам необходимо дополнить содержимое родительского блока, а не полностью переопределить его. Содержимое {{ block.super }} не будет автоматически экранировано (смотрите раздел ниже), так как оно уже было экранировано, при необходимости, в родительском шаблоне.

  • Для ясности, вы можете добавить название вашему тегу {% endblock %}. Например:

    {% block content %}
    ...
    {% endblock content %}
    

    В больших шаблонах такой подход поможет вам увидеть какой тег {% block %} был закрыт.

Вы не можете определить несколько тегов 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

...в результате оставшееся содержимое страницы будет выделено полужирным!

Очевидно, пользовательским данными нельзя слепо доверять и вставлять непосредственно в содержимое страницы, так как злоумышленники могут использовать это с плохими намерениями. Такой тип уязвимости называется Cross Site Scripting (XSS) атакой.

Чтобы избежать этой проблемы, у вас есть два варианта:

  • Первый, вы можете применять ко всем сомнительным переменным фильтр escape (описанный далее), который преобразует потенциально опасные HTML символы в безопасные. Такое решение было принятым в первых версиях Django, но проблема в том, что оно возлагает бремя ответственности за безопасность на вас, разработчика / автора шаблона. Легко забыть экранировать переменную.

  • Второй, вы можете позволить Django автоматически экранировать HTML. Оставшаяся часть этого раздела описывает, как автоматическое экранирование работает.

По-умолчанию в Django, каждый шаблон экранирует все переменные. В частности выполняются такие замены:

  • < заменяется на &lt;

  • > заменяется на &gt;

  • ' (одинарная кавычка) заменяется на &#39;

  • " (двойная кавычка) заменяется на &quot;

  • & заменяется на &amp;

Опять же, мы подчеркиваем, что такое поведение используется по умолчанию. Если вы используете систему шаблонов 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: &lt;b&gt;
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 %}

Так как авто-экранирование отключено в базовом шаблоне, оно будет отключено и в дочернем шаблоне. Если переменная 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 &lt; 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 }}

И конечно вы можете использовать методы вашей модели:

# In model
class Task(models.Model):
    def foo(self):
        return "bar"

# In template
{{ task.foo }}

Так как Django намеренно ограничивает определение логики проекта в шаблоне, передавать аргументы при вызове метода в шаблоне нельзя. Данные должны вычисляться в представлении и передаваться в шаблон для отображения.

Собственные библиотеки тегов и фильтров

Некоторые приложения предоставляют собственные библиотеки тегов и фильтров. Чтобы использовать их в шаблоне, используйте тег load:

{% load comments %}

{% comment_form for blogs.entries entry.id with is_public yes %}

В это примере, тег load загружает библиотеку comments, которая предоставляет тег comment_form. В разделе документации вашего интерфейса администратора вы можете найти список всех установленных библиотек.

Тег load может принимать названия нескольких библиотек, разделенные пробелом. Например:

{% load comments i18n %}

Смотрите раздел Собственные шаблонные теги и фильтры, чтобы узнать как создавать собственные библиотеки для шаблонов.

Собственные библиотеки и наследование шаблонов

При загрузке библиотеки, теги и фильтры, которые она содержит, будут доступны только в текущем шаблоне – не в родительском или дочернем шаблонах.

Например, если шаблон foo.html содержит {% load comments %}, дочерний шаблон (например, содержащий``{% extends “foo.html” %}``) не сможет использовать теги и фильтры из этой библиотеки. Дочерний шаблон должен самостоятельно загрузить библиотеку, используя {% load comments %}.

Так сделано ради “maintainability” и “sanity”.