Spec-Zone.ru › Django 4.0

Язык шаблонов 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 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»), так как они уже были экранированы, если это необходимо, в родительском шаблоне.
  • Используя то же имя шаблона, что и у наследуемого шаблона, {% extends %} можно использовать для наследования шаблона одновременно с его переопределением. В сочетании с {{ block.super }}, это может быть мощным способом внесения небольших изменений. См. Расширение переопределённого шаблона в разделе Переопределение шаблонов для полного примера.
  • Переменные, созданные вне {% 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 alert!

Аналогично, что если имя содержало символ '<'?

<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 при том, что переменная 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 %}

Аналогично, 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" %}) не будет иметь доступа к тегам и фильтрам шаблона humanize. Дочерний шаблон отвечает за свою собственную {% load humanize %}.

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

См. также

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

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.0/ref/templates/language/

Spec-Zone.ru

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