Шаблоны

Будучи веб фреймверком, Django позволяет динамически генерировать HTML. Самый распространенный подход - использование шаблонов. Шаблоны содержат статический HTML и динамические данные, рендеринг которых описан специальным синтаксисом. Пример использования шаблонов для создания HTML шаблонов можно найти в учебнике Часть 3.

Проект Django может использовать один или несколько механизмов создания шаблонов (или ни одного, если вы не используете шаблоны). Django предоставляет бэкенд для собственной системы шаблонов, которая называется - язык шаблонов Django (Django template language, DTL), и популярного альтернативного шаблонизатора Jinja2. Сторонние приложения могут предоставлять бэкенд и для других систем шаблонов.

Django предоставляет стандартный API для загрузки и рендеринга шаблонов, незавимисо от используемого бэкенда. Загрузка включает в себя поиск шаблона по названию и предварительную обработку, обычно выполняется загрузка шаблона в память. Рендеринг означает передачу данных контекста в шаблон и возвращение строки с результатом.

Язык шаблонов Django – собственная система шаблонов Django. До Django 1.8 – это была единственная альтернатива. Это хорошая система шаблонов, но со своими особенностями. Если у вас нет причин использовать другую систему шаблон, используйте встроенную, особенно, если разрабатываете распространяемое приложение, содержащее шаблоны. Встроенные шаблоны Django, которые содержат шаблоны, например django.contrib.admin, используют систему шаблонов Django.

По историческим причинам поддержка шаблонов и встроенная система шаблонов Django находятся в одном пакете django.template.

Предупреждение

The template system isn’t safe against untrusted template authors. For example, a site shouldn’t allow its users to provide their own templates, since template authors can do things like perform XSS attacks and access properties of template variables that may contain sensitive information.

Поддержка систем шаблонов

Настройки

Шаблоны можно настроить с помощью настройки TEMPLATES. Это список, который содержит настройки для систем шаблонов. По умолчанию настройка пустая. settings.py, сгенерированный командой startproject, содержит более полезное значение:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

BACKEND путь для импорта Python к классу бэкенда шаблонов. Встроенные бэкенды это django.template.backends.django.DjangoTemplates и django.template.backends.jinja2.Jinja2.

Т.к. большинство систем шаблонов загружают шаблоны с файлов, настройки содержат:

  • DIRS, которая содержим список каталогов с шаблонами. Бэкенд ищет в них шаблон в указанном порядке.
  • APP_DIRS указывает бэкенду искать ли шаблоны в установленных приложениях. Каждый бэкенд определяет определенное название для каталога с шаблонами в приложении.

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

OPTIONS содержит настройки специфические для бэкенда.

Использование

Модуль django.template.loader предоставляет две функции для загрузки шаблонов.

get_template(template_name, using=None)

Эта функция загружает шаблон с указанным названием и возвращает объект Template.

Возвращаемое значение зависит от используемого бэкенда т.к. каждый бэкенд использует свой класс Template.

get_template() пытается загрузить шаблон каждым из бэкендов, пока шаблон не будет загружен. Если шаблон не найден, будет вызвано исключение TemplateDoesNotExist. Если шаблон найден, но синтаксис не верен, будет вызвано TemplateSyntaxError.

Как шаблоны ищутся и загружаются, зависит от используемого бэкенда шаблонов и его настроек.

Если вы хотите использовать конкретный бэкенд, передайте его название из NAME в аргументе using.

select_template(template_name_list, using=None)

select_template() как и get_template(), но принимает список названий шаблонов. Пытается загрузить по очереди каждый из шаблонов, возвращает первый загруженный.

Если не удалось загрузить шаблон, вызываются исключения, описанные в django.template:

exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)

Это исключение вызывается, если шаблоне не был найден. Принимает дополнительные параметры, которые отображаются на странице ошибки:

backend
Бэкенд шаблонов, который вызывал исключение.
tried
Список источников шаблонов, которые проверялись при поиске шаблона. Состоит из списка кортежей вида (origin, status), где origin – это объект Origin, а status – строка, которая описывает ошибку.
chain
Список промежуточных ошибок TemplateDoesNotExist, которые были вызваны при поиске шаблона. Используется функцией, например get_template(), которая пытается загрузить шаблон, используя разные бэкенды.
exception TemplateSyntaxError(msg)

Это исключение вызывается, если шаблон найден, но содержит ошибки.

Объекты Template, которые возвращают get_template() и select_template(), должны содержать метод render() со следующей сигнатурой:

Template.render(context=None, request=None)

Рендерит шаблон с переданным контекстом.

Если context передан, это должен быть dict. Если не передан, используется пустой контекст.

Если передан request, это должен быть экземпляр HttpRequest. Шаблонизатор должен добавить его в шаблон, как и CSRF токен. Как это должно происходит определяет шаблонизатор.

Пример алгоритма поиска шаблона. Возьмем следующую настройку TEMPLATES:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

Если вызвать get_template('story_detail.html'), Django будет искать следующие файлы:

  • /home/html/example.com/story_detail.html (шаблонизатор 'django')
  • /home/html/default/story_detail.html (шаблонизатор 'django')
  • /home/html/jinja2/story_detail.html (шаблонизатор 'jinja2')

Если вызвать select_template(['story_253_detail.html', 'story_detail.html']), Django бдудет искать:

  • /home/html/example.com/story_253_detail.html (шаблонизатор 'django')
  • /home/html/default/story_253_detail.html (шаблонизатор 'django')
  • /home/html/jinja2/story_253_detail.html (шаблонизатор 'jinja2')
  • /home/html/example.com/story_detail.html (шаблонизатор 'django')
  • /home/html/default/story_detail.html (шаблонизатор 'django')
  • /home/html/jinja2/story_detail.html (шаблонизатор 'jinja2')

Если Django находит шаблон, поиск останавливается.

Советы

Вы можете использовать select_template() для гибкой загрузки шаблонов. Например, вы написали новость и хотите использовать другой шаблон для некоторых новостей. Вы можете использовать select_template(['story_%s_detail.html' % story.id, 'story_detail.html']). Это позволить создать отдельный шаблон для определенных новостей, а для всех остальных использовать шаблон по умолчанию.

Мы советуем хранить шаблоны в подкаталогах в каталогах с шаблонами. Следует создать подкаталог для каждого приложения Django, в котором вы можете хранить шаблоны в различных подкаталогах, если это необходимо.

Хранить все шаблоны в корневом каталоге может привести к хаосу в шаблонах.

To load a template that’s within a subdirectory, use a slash, like so:

get_template('news/story_detail.html')

При использовании настроек TEMPLATES, приведенных выше, Django будет искать следующие шаблоны:

  • /home/html/example.com/news/story_detail.html (шаблонизатор 'django')
  • /home/html/default/news/story_detail.html (шаблонизатор 'django')
  • /home/html/jinja2/news/story_detail.html (шаблонизатор 'jinja2')

Django предоставляет несколько вспомогательных функций для работы с шаблонами.

render_to_string(template_name, context=None, request=None, using=None)

render_to_string() загружает шаблон как и get_template(), и вызывает метод render(). Принимает следующие аргументы.

template_name
Название шаблона, который будет загружен и выполнен. Если указать список шаблонов, для поиска шаблонов Django будет использовать select_template() вместо get_template().
context
dict, который будет использовать как контекст при рендеринге шаблона.
request
Необязательный HttpRequest, который будет использоваться при рендеринге шаблона.
using
An optional template engine NAME. The search for the template will be restricted to that engine.

Usage example:

from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})

See also the render() shortcut which calls render_to_string() and feeds the result into an HttpResponse suitable for returning from a view.

Вы можете использовать шаблонизатор непосредственно:

engines

Шаблонизаторы доступны в django.template.engines:

from django.template import engines

django_engine = engines['django']
template = django_engine.from_string("Hello {{ name }}!")

Ключ 'django' в этом примере соответствует NAME.

Встроенные бэкенды

class DjangoTemplates

Укажите 'django.template.backends.django.DjangoTemplates' в BACKEND, чтобы использовать шаблонизатор Django.

Если APP_DIRS равна True, DjangoTemplates будет искать шаблоны в подкаталогах templates установленных приложений. Такое название используется для обратной совместимости.

DjangoTemplates принимает следующие параметры OPTIONS:

  • 'autoescape': a boolean that controls whether HTML autoescaping is enabled.

    It defaults to True.

    Предупреждение

    Only set it to False if you’re rendering non-HTML templates!

  • 'context_processors': список путей Python, который указывают на процессоры контекста, которые добавляют дополнительные переменные в контекст при рендеринге шаблона в процессе обработки запроса. Процессоры контекста – это функции, или другие вызываемые объекты, которые принимают объект запроса и возвращают dict значений, который будут добавлены в контекст.

    По умолчанию содержит пустой список.

    Подробности смотрите в описании RequestContext.

  • 'debug': булево значение, которое включает/выключает отладку шаблонов. При True будет отображаться страница с отладочной информацией, если возникнет ошибка при рендеринге шаблона. Страница показывает часть шаблона, которая вызвала ошибку.

    По умолчанию равна значению DEBUG.

  • 'loaders': список путей Python к загрузчикам шаблонов. Каждый класс Loader знает как загрузить шаблон из определенного источника. Можно использовать кортеж вместо строки. Первым элементом должен быть путь к Loader, последующие элементы передаются в Loader при инициализации.

    Значение по умолчанию зависит от значений DIRS и APP_DIRS.

    Подробности смотрите в Типы загрузчиков.

  • 'string_if_invalid': строка, которая будет использоваться для неправильных (например, с опечаткой) переменных в шаблоне.

    По умолчанию пустая строка.

    Подробности смотрите в Как обрабатываются неправильные переменные.

  • 'file_charset': кодировка, которая используется при чтении файла шаблона с диска.

    It defaults to 'utf-8'.

  • 'libraries': словарь библиотек тегов, которые зарегистрированы для бэкенда. Состоит из названия библиотеки и пути для импорта. Позволяет добавлять новые библиотеки, или поменять название для существующих. Например:

    OPTIONS={
        'libraries': {
            'myapp_tags': 'path.to.myapp.tags',
            'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
        },
    }
    

    Библиотеки могут быть загружены с помощью тега {% load %}, в котором нужно указать соответствующий ключ словаря.

  • 'builtins': список путей для импорта библиотек тегов, которые будут автоматически загружены и добавлены к встроенным тегам и фильтрам. Например:

    OPTIONS={
        'builtins': ['myapp.builtins'],
    }
    

    Теги и фильтры из этих библиотек могу быть использованы без вызова {% load %}.

class Jinja2

Для использования необходимо установить Jinja2:

$ python -m pip install Jinja2
...\> py -m pip install Jinja2

Укажите 'django.template.backends.jinja2.Jinja2' в BACKEND для настройки шаблонизатора Jinja2.

Если APP_DIRS равна True, Jinja2 будет искать шаблоны в подкаталоге ``jinja2``установленных приложений.

Самой важной настройкой в OPTIONS является 'environment'. Это путь для импорта функции, или другого вызываемого объекта, которая возвращает Jinja2. По умолчанию 'jinja2.Environment'. Django вызывает функцию и передает другие параметры как именованные аргументы. Также Django указывает значения по умолчанию для некоторых параметров Jinja2:

  • 'autoescape': True
  • 'loader': загрузчик соответствующий настройкам DIRS и APP_DIRS
  • 'auto_reload': settings.DEBUG
  • 'undefined': DebugUndefined if settings.DEBUG else Undefined

Jinja2 engines also accept the following OPTIONS:

  • 'context_processors': список путей Python, который указывают на процессоры контекста, которые добавляют дополнительные переменные в контекст при рендеринге шаблона в процессе обработки запроса. Процессоры контекста – это функции, или другие вызываемые объекты, которые принимают объект запроса и возвращают dict значений, который будут добавлены в контекст.

    По умолчанию содержит пустой список.

    Using context processors with Jinja2 templates is discouraged.

    Context processors are useful with Django templates because Django templates don’t support calling functions with arguments. Since Jinja2 doesn’t have that limitation, it’s recommended to put the function that you would use as a context processor in the global variables available to the template using jinja2.Environment as described below. You can then call that function in the template:

    {{ function(request) }}
    

    Some Django templates context processors return a fixed value. For Jinja2 templates, this layer of indirection isn’t necessary since you can add constants directly in jinja2.Environment.

    The original use case for adding context processors for Jinja2 involved:

    • Making an expensive computation that depends on the request.
    • Needing the result in every template.
    • Using the result multiple times in each template.

    Unless all of these conditions are met, passing a function to the template is more in line with the design of Jinja2.

The default configuration is purposefully kept to a minimum. If a template is rendered with a request (e.g. when using render()), the Jinja2 backend adds the globals request, csrf_input, and csrf_token to the context. Apart from that, this backend doesn’t create a Django-flavored environment. It doesn’t know about Django filters and tags. In order to use Django-specific APIs, you must configure them into the environment.

Например, вы можете создать следующий myproject/jinja2.py:

from django.templatetags.static import static
from django.urls import reverse

from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': static,
        'url': reverse,
    })
    return env

и указать в настройке 'environment' 'myproject.jinja2.environment'.

Теперь вы можете использовать в шаблонах Jinja2:

<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">

<a href="{{ url('admin:index') }}">Administration</a>

The concepts of tags and filters exist both in the Django template language and in Jinja2 but they’re used differently. Since Jinja2 supports passing arguments to callables in templates, many features that require a template tag or filter in Django templates can be achieved by calling a function in Jinja2 templates, as shown in the example above. Jinja2’s global namespace removes the need for template context processors. The Django template language doesn’t have an equivalent of Jinja2 tests.

Собственный бэкенд

Вот небольшой пример собственного шаблонизатора. Шаблонизатор должен наследоваться от django.template.backends.base.BaseEngine и определить метод get_template(), и опционально from_string(). Код нашего шаблонизатора foobar:

from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy

import foobar


class FooBar(BaseEngine):

    # Name of the subdirectory containing the templates for this engine
    # inside an installed application.
    app_dirname = 'foobar'

    def __init__(self, params):
        params = params.copy()
        options = params.pop('OPTIONS').copy()
        super().__init__(params)

        self.engine = foobar.Engine(**options)

    def from_string(self, template_code):
        try:
            return Template(self.engine.from_string(template_code))
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)

    def get_template(self, template_name):
        try:
            return Template(self.engine.get_template(template_name))
        except foobar.TemplateNotFound as exc:
            raise TemplateDoesNotExist(exc.args, backend=self)
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)


class Template:

    def __init__(self, template):
        self.template = template

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        if request is not None:
            context['request'] = request
            context['csrf_input'] = csrf_input_lazy(request)
            context['csrf_token'] = csrf_token_lazy(request)
        return self.template.render(context)

Подробности смотрите в DEP 182.

Debug integration for custom engines

The Django debug page has hooks to provide detailed information when a template error arises. Custom template engines can use these hooks to enhance the traceback information that appears to users. The following hooks are available:

Template postmortem

The postmortem appears when TemplateDoesNotExist is raised. It lists the template engines and loaders that were used when trying to find a given template. For example, if two Django engines are configured, the postmortem will appear like:

../_images/postmortem.png

Custom engines can populate the postmortem by passing the backend and tried arguments when raising TemplateDoesNotExist. Backends that use the postmortem should specify an origin on the template object.

Contextual line information

If an error happens during template parsing or rendering, Django can display the line the error happened on. For example:

../_images/template-lines.png

Custom engines can populate this information by setting a template_debug attribute on exceptions raised during parsing and rendering. This attribute is a dict with the following values:

  • 'name': The name of the template in which the exception occurred.
  • 'message': The exception message.
  • 'source_lines': The lines before, after, and including the line the exception occurred on. This is for context, so it shouldn’t contain more than 20 lines or so.
  • 'line': The line number on which the exception occurred.
  • 'before': The content on the error line before the token that raised the error.
  • 'during': The token that raised the error.
  • 'after': The content on the error line after the token that raised the error.
  • 'total': The number of lines in source_lines.
  • 'top': The line number where source_lines starts.
  • 'bottom': The line number where source_lines ends.

Given the above template error, template_debug would look like:

{
    'name': '/path/to/template.html',
    'message': "Invalid block tag: 'syntax'",
    'source_lines': [
        (1, 'some\n'),
        (2, 'lines\n'),
        (3, 'before\n'),
        (4, 'Hello {% syntax error %} {{ world }}\n'),
        (5, 'some\n'),
        (6, 'lines\n'),
        (7, 'after\n'),
        (8, ''),
    ],
    'line': 4,
    'before': 'Hello ',
    'during': '{% syntax error %}',
    'after': ' {{ world }}\n',
    'total': 9,
    'bottom': 9,
    'top': 1,
}

Origin API and 3rd-party integration

Django templates have an Origin object available through the template.origin attribute. This enables debug information to be displayed in the template postmortem, as well as in 3rd-party libraries, like the Django Debug Toolbar.

Custom engines can provide their own template.origin information by creating an object that specifies the following attributes:

  • 'name': The full path to the template.
  • 'template_name': The relative path to the template as passed into the the template loading methods.
  • 'loader_name': An optional string identifying the function or class used to load the template, e.g. django.template.loaders.filesystem.Loader.

Язык шаблонов Django

Синтаксис

Об этом документе

Этот раздел описывает основы синтаксиса языка шаблонов Django. Подробности смотрите в полном описании.

A Django template is a text document or a Python string marked-up using the Django template language. Some constructs are recognized and interpreted by the template engine. The main ones are variables and tags.

Шаблон рендерится с контекстом. Рендеринг заменяет переменные на их значения, которые ищутся в контексте, и выполняет теги. Все остальное выводится как есть.

Синтаксис языка шаблонов Django использует четыре конструкции.

Переменные

Переменные выводят значения из контекста, который является словарем.

Переменные выделяются {{ и }}, например:

My first name is {{ first_name }}. My last name is {{ last_name }}.

Для контекста {'first_name': 'John', 'last_name': 'Doe'} шаблон отрендерит:

My first name is John. My last name is Doe.

Обращение к ключам словаря, атрибутам объектов и элементам списка выполняется через точку:

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

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

Теги

Теги позволяют добавлять произвольную логику в шаблон.

Например, теги могут выводить текст, добавлять логические операторы, такие как «if» или «for», получать содержимое из базы данных, или предоставлять доступ к другим тегам.

Теги выделяются {% и %}, например:

{% csrf_token %}

Большинство тегов принимают аргументы:

{% cycle 'odd' 'even' %}

Некоторые теги требуют закрывающий тег:

{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}

Ознакомьтесь со списком всех встроенных тегов и с руководством по созданию тегов.

Фильтры

Фильтры преобразуют переменные и аргументы тегов.

Могут выглядеть таким образом:

{{ django|title }}

Для контекста {'django': 'the web framework for perfectionists with deadlines'} этот шаблон выведет:

The Web Framework For Perfectionists With Deadlines

Некоторые фильтры принимают аргументы:

{{ my_date|date:"Y-m-d" }}

Ознакомьтесь со списком встроенных фильтров и руководством по созданию фильтра.

Комментарии

Комментарии могут выглядеть таким образом:

{# this won't be rendered #}

Тег {% comment %} позволяет добавлять многострочные комментарии.

Компоненты

Об этом документе

Это обзор API языка шаблонов Django. Подробности смотрите в полном описании API.

Engine

django.template.Engine инкапсулирует шаблонизатор Django. Главная причина использовать непосредственно Engine – использовать шаблонизатор Django все проекта Django.

django.template.backends.django.DjangoTemplates – адаптер django.template.Engine для API шаблонов Django.

Шаблон

django.template.Template представляет скомпилированный шаблон. Шаблоны загружаются с помощью Engine.get_template() или Engine.from_string()

django.template.backends.django.Template – адаптер django.template.Template для API шаблонов Django.

Контекст

django.template.Context содержит данные контекста и некоторые метаданные. Передается в Template.render() при рендеринге шаблона.

django.template.RequestContext – дочерний класс Context, который содержит текущий HttpRequest и выполняет процессоры контекста.

API шаблонов Django не содержит аналога. Данные контекста передаются как dict, а текущий HttpRequest передается отдельно при необходимости.

Загрузчики

Загрузчики шаблонов отвечают за их поиск, загрузку и создание объекта Template.

Django предоставляет несколько встроенных загрузчиков шаблонов и поддерживает собственные загрузчики.

Процессоры контекста

Процессоры контекста – это функции, которые принимают текущий HttpRequest, и возвращают dict данными, которые будут добавлены в контекст.

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

Django provides many built-in context processors, and you can implement your own additional context processors, too.