Сообщить об ошибке.

Наследование шаблонов jinja2 в Python

Самая мощная часть модуля jinja2 - это наследование шаблонов. Наследование шаблонов позволяет создать базовый "скелетный" шаблон, который содержит все общие элементы сайта и определяет блоки {% block ... %}, которые "дочерние" шаблоны могут переопределить.

Содержание:


Базовый шаблон Jinja2.

Базовый шаблон Jinja2, который назовем base.html, определяет простой документ-скелет HTML-разметки, который будет использоваться для создания более сложных страниц. Задача "дочерних" шаблонов - заполнить пустые блоки {% block ... %} контентом:

<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>

В этом примере теги {% block ... %} ... {% endblock %} определяют четыре блока, которые могут заполнять дочерние шаблоны. Все, что делает тег блока block, - это сообщает механизму шаблонов, что дочерний шаблон может переопределить эти заполнители в шаблоне.

Теги блоков block могут находиться внутри других блоков, таких как {% if ... %} ... {% endif %}, но они всегда будут заполняться контентом независимо от того, разрешает ли блок шаблона if/else в конечном итоге отображается внутреннему блоку block.

Дочерний шаблон Jinja2.

Дочерний шаблон Jinja2 может выглядеть так:

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome to my awesome homepage.
    </p>
{% endblock %}

В дочернем шаблоне тег {% extends ... %} является ключевым. Он сообщает механизму шаблонов, что этот шаблон "расширяет" другой шаблон. Когда система шаблонов Jinja2 оценивает этот шаблон, она сначала находит родителя. Тег extends должен быть первым тегом в шаблоне.

Jinja2 поддерживает динамическое наследование и не делает различий между родительским и дочерним шаблоном до тех пор, пока рендеру не попадется тег extends. Это приводит к тому, что все, что было до первого тега extends, включая текст, пробелы и т.д., будет выводится вместо того, чтобы игнорироваться.

Обратите внимание: т. к. в примере дочерний шаблон не определяет блок нижнего колонтитула, вместо него будет использоваться значение из родительского шаблона.

Путь к имени файла шаблона зависит от загрузчика шаблона. Например, загрузчик FileSystemLoader определяет путь к папке с шаблонами и позволяет получить доступ к шаблонам, указав просто имя файла. Для получения доступа к шаблонам в подкаталогах можно использовать косую черту:

{% extends "layout/default.html" %}

Нельзя определить несколько тегов {% block name %} с одним и тем же именем name в одном шаблоне. Это ограничение существует, потому что тег блока работает "в обоих" направлениях. То есть тег блока block не просто предоставляет заполнитель, он также определяет содержимое, которое заполняется в родительском элементе. Если бы в шаблоне было два тега {% block name %} с одинаковыми именамиname `, то родительский элемент этого шаблона не знал бы, какой из содержимого блока использовать/заполнять.

Но, если необходимо напечатать блок несколько раз, то можно использовать специальную переменную self и вызвать блок с этим именем:

<title>{% block title %}{% endblock %}</title>
{# повторный вывод блока с именем `title` #}
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}

Суперблоки в шаблонах Jinja2.

Можно отобразить содержимое родительского блока, вызвав функцию шаблона {{ super() }}. Эта функция возвращает результаты родительского блока:

{% block sidebar %}
    <h3>Table Of Contents</h3>
    ...
    {{ super() }}
{% endblock %}

Расширение вложенности в шаблонах Jinja2.

В случае нескольких уровней {% extends %} супер ссылки могут быть связаны (как в super.super()) для пропуска уровней в дереве наследования.

Например:

# parent.tmpl
body: {% block body %}Hi from parent.{% endblock %}

# child.tmpl
{% extends "parent.tmpl" %}
{% block body %}Hi from child. {{ super() }}{% endblock %}

# grandchild1.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild1.{% endblock %}

# grandchild2.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild2. {{ super.super() }} {% endblock %}
  • Визуализация child.tmpl выдаст текст: body: Hi from child. Hi from parent.
  • Визуализация grandchild1.tmpl выдаст текст: body: Hi from grandchild1.
  • Визуализация grandchild2.tmpl выдаст текст: body: Hi from grandchild2. Hi from parent.

Именованные конечные теги блоков.

Jinja позволяет ставить имя блока после закрывающего тега для лучшей читаемости:

{% block sidebar %}
    {% block inner_sidebar %}
        ...
    {% endblock inner_sidebar %}
{% endblock sidebar %}

При этом имя после слова endblock конечного блока обязательно должно совпадать с именем этого блока.

Вложенность блоков и область видимости в шаблонах.

Блоки {% block ... %}{% endblock %} могут быть вложены для создания более сложных макетов. При этом блоки по умолчанию не могут обращаться к переменным из внешних областей:

{% for item in seq %}
    <li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}

В этом примере будут выведены пустые элементы <li></li>, потому что элемент {{ item }} недоступен внутри блока {% block loop_item %}. Причина в том, что блок изменяется дочерним шаблоном путем передачи переменной, которая не определена в родительском блоке, а так же не передана в контекст родителя.

Начиная с Jinja 2.2, можно явно указать, что переменные дочернего блока доступны в родительском блоке, добавив модификатор scoped в объявление блока родителя:

{% for item in seq %}
    <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
{% endfor %}

При переопределении блока родителя модификатор scoped указывать не обязательно.