Spec-Zone.ru › Django 1.9

Язык шаблонов 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 %} ... 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 and extends
Устанавливает наследование шаблонов (см. ниже), мощный способ сократить «повторяющийся код» в шаблонах.

Это лишь выборка из всего списка; см. справку по встроенным тегам для получения полного списка.

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

См. также

Интерфейс администрирования 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»), поскольку они уже были экранированы, если это необходимо, в родительском шаблоне.
  • Для лучшей читаемости вы можете присвоить имя тегу {% 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 каждый шаблон автоматически экранирует вывод каждого тега переменной. В частности, экранируются эти пять символов:

  • < преобразуется в &lt;
  • > преобразуется в &gt;
  • ' (одинарная кавычка) преобразуется в &#39;
  • " (двойная кавычка) преобразуется в &quot;
  • & преобразуется в &amp;

Ещё раз подчеркнём, что это поведение включено по умолчанию. Если вы используете систему шаблонов 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: &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 %}

Тег автоматического экранирования передаёт своё влияние на шаблоны, которые расширяют текущий шаблон, а также на шаблоны, включённые с помощью тега include, как и все теги блоков. Например:

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

Поскольку автоматическое экранирование выключено в базовом шаблоне, оно также будет выключено в дочернем шаблоне, что приведёт к следующему рендеру HTML, когда переменная greeting содержит строку <b>Hello!</b>:

<h1>This &amp; that</h1>
<b>Hello!</b>

Примечания

В целом, авторы шаблонов не должны сильно беспокоиться об автоматическом экранировании. Разработчики на стороне Python (люди, пишущие представления и пользовательские фильтры) должны учитывать случаи, когда данные не должны экранироваться, и помечать данные соответствующим образом, чтобы всё работало правильно в шаблоне.

Если вы создаёте шаблон, который может быть использован в ситуациях, где вы не уверены, включено ли автоматическое экранирование, добавьте фильтр escape к любой переменной, которая требует экранирования. Когда автоматическое экранирование включено, нет опасности, что фильтр 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 %}

Аналогично, наборы запросов предоставляют метод 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.9/ref/templates/language/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API