Язык шаблонов 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. Вы можете использовать язык шаблона для любого текстового формата, такого как электронная почта, JavaScript и CSV.

Переменные

Переменные выглядят так: {{ 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.

Тэги

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

Для некоторых тегов требуются начальные и конечные теги (например, {% tag %} ... tag contents ... {% endtag %}).

Django поставляется с двумя десятками встроенных тегов шаблонов. Вы можете прочитать все о них в справочнике по встроенным тегам <ref-templates-builtins-tags>. Чтобы дать вам представление о том, что доступно, вот некоторые из наиболее часто используемых тегов:

for

Перебераеи каждый элемент в массиве. Например, чтобы отобразить список спортсменов, представленный в athlete_list:

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

Оценивает переменную, и если эта переменная имеет значение «true», отображается содержимое блока:

{% 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 не пуст, будет отображено сообщение «Athletes should be out…». Если оба списка пусты, будет отображаться «No athletes.».

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

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

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

block и extends
Настройте наследование шаблонов (смотрите ниже), мощный способ сократить количество лишних повторений в шаблонах.

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

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

См.также

Интерфейс администратора Django может включать в себя полную ссылку на все теги шаблонов и фильтры, доступные для данного сайта. Смотрите Генератор документации для администраторов Django.

Комментарии

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

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

  • Используя то же имя шаблона, от которого вы наследуете, {% extends %} может использоваться для наследования шаблона одновременно с его переопределением. В сочетании с {{ block.super }} это может быть мощным способом внесения небольших настроек. Смотрите exnding_an_overridden_template в Переопределении шаблонов Практического руководства для полного примера.

  • Переменные, созданные вне {% block %} с использованием синтаксиса тега шаблона as, не могут использоваться внутри блока. Например, этот шаблон ничего не отображает:

    {% translate "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 каждый шаблон автоматически экранирует вывод каждого тега переменной. В частности, эти пять символов экранированы:

  • < конвертируется в &lt;
  • > конвертируется в &gt;
  • ' (одинарная кавычка) конвертируется в &#x27;
  • " (двойная кавычка) конвертируется в &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, как и все теги блоков. Например:

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

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

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

Примечания

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

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

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

models.py
class Task(models.Model):
    def foo(self):
        return "bar"
template.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 %}.

Это функция ради ремонтопригодности и здравомыслия.

См.также

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