Spec-Zone.ru › Django 4.2

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

    {% if change_title %}
        {% block title %}Hello!{% endblock title %}
    {% endif %}
    

Наконец, обратите внимание, что вы не можете определить несколько тегов 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 как сокращение от safe from further escaping или can be safely interpreted as 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.2/ref/templates/language/

Spec-Zone.ru

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