Spec-Zone.ru › Django 4.1

Язык шаблонов Django: для программистов Python

Этот документ объясняет систему шаблонов Django с технической точки зрения — как она работает и как её можно расширить. Если вы ищете справку по синтаксису языка, см. Язык шаблонов Django.

Предполагается понимание шаблонов, контекстов, переменных, тегов и рендеринга. Если вы не знакомы с этими концепциями, начните с введения в язык шаблонов Django.

Обзор

Использование системы шаблонов на Python — это трехэтапный процесс:

  1. Вы настраиваете Engine.
  2. Вы компилируете код шаблона в Template.
  3. Вы рендерите шаблон с помощью Context.

Проекты Django обычно полагаются на API высокого уровня, не зависящие от бэкенда для каждого из этих шагов, а не на низкоуровневые API системы шаблонов:

  1. Для каждого бэкенда DjangoTemplates в настройке TEMPLATES Django создаёт экземпляр Engine. DjangoTemplates оборачивает Engine и адаптирует его к общему API бэкенда шаблонов.
  2. Модуль django.template.loader предоставляет функции, такие как get_template() для загрузки шаблонов. Они возвращают django.template.Template.
  3. Полученный на предыдущем шаге django.template.Template имеет метод render(), который обрабатывает контекст и, возможно, запрос в Context и делегирует рендеринг базовому Template.

Настройка движка

Если вы используете бэкенд DjangoTemplates, этот документ, вероятно, не тот, который вам нужен. Экземпляр класса Engine ниже доступен через атрибут engine этого бэкенда, а любые значения по умолчанию, упомянутые ниже, переопределяются значениями, переданными через DjangoTemplates.

class Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True)

При создании экземпляра Engine все аргументы должны передаваться в качестве ключевых аргументов:

  • dirs — список каталогов, где движок должен искать исходные файлы шаблонов. Используется для настройки filesystem.Loader.

    По умолчанию — пустой список.

  • app_dirs — влияет только на значение по умолчанию loaders. Смотрите ниже.

    По умолчанию — False.

  • autoescape — управляет включением автоматического экранирования HTML.

    По умолчанию — True.

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

    Установите его в значение False только если вы рендерите шаблоны, отличные от HTML!

  • context_processors — список имён функций Python, используемых для заполнения контекста при рендеринге шаблона с запросом. Эти функции принимают объект запроса в качестве аргумента и возвращают словарь (dict) элементов, которые будут объединены в контекст.

    По умолчанию — пустой список.

    См. RequestContext для получения дополнительной информации.

  • debug — логическая переменная, включающая/выключающая режим отладки шаблонов. Если она True, движок шаблонов будет сохранять дополнительную информацию об отладке, которая может быть использована для отображения подробного отчета об исключениях, возникающих при рендеринге шаблонов.

    По умолчанию — False.

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

    По умолчанию — список, содержащий:

    • 'django.template.loaders.filesystem.Loader'
    • 'django.template.loaders.app_directories.Loader' только если app_dirs равно True.

    Затем эти загрузчики оборачиваются в django.template.loaders.cached.Loader.

    Изменено в Django 4.1:

    В более старых версиях кеширующий загрузчик шаблонов по умолчанию включался только при DEBUG равном False.

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

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

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

    Подробности см. в разделе Обработка недопустимых переменных шаблонов.

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

    По умолчанию — 'utf-8'.

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

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

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

  • 'builtins': Список имён модулей тегов Python для добавления в встроенные функции. Например:

    Engine(
        builtins=['myapp.builtins'],
    )
    

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

static Engine.get_default()

Возвращает базовый Engine из первого настроенного DjangoTemplates движка. Вызывает исключение ImproperlyConfigured, если движки не настроены.

Требуется для сохранения API, которые полагаются на глобально доступный, неявно настроенный движок. Любое другое использование строго не рекомендуется.

Engine.from_string(template_code)

Компилирует заданный код шаблона и возвращает объект Template.

Engine.get_template(template_name)

Загружает шаблон с заданным именем, компилирует его и возвращает объект Template.

Engine.select_template(template_name_list)

Аналогично get_template(), за исключением того, что он принимает список имён и возвращает первый найденный шаблон.

Загрузка шаблона

Рекомендуемый способ создания Template заключается в вызове методов-фабрик Engine: get_template(), select_template() и from_string().

В проекте Django, где настройка TEMPLATES определяет движок DjangoTemplates, можно напрямую создать экземпляр Template. Если определено более одного движка DjangoTemplates, будет использован первый.

class Template

Этот класс расположен в django.template.Template. Конструктор принимает один аргумент — исходный код шаблона:

from django.template import Template

template = Template("My name is {{ my_name }}.")

За кулисами

Система анализирует исходный код шаблона только один раз — при создании объекта Template. После этого он хранится во внутренней структуре дерева для повышения производительности.

Даже сам процесс анализа довольно быстрый. Большая часть анализа выполняется с помощью единственного вызова короткой регулярной записи.

Вывод контекста

После получения объекта скомпилированного Template, можно вывести контекст с помощью него. Один и тот же шаблон можно использовать для вывода контекста несколько раз с различными контекстами.

class Context(dict_=None)

Конструктор django.template.Context принимает необязательный аргумент — словарь, сопоставляющий имена переменных с их значениями.

Подробности см. ниже в Разбираемся с объектами контекста.

Template.render(context)

Вызовите метод render() объекта Template с объектом Context для «заполнения» шаблона:

>>> from django.template import Context, Template
>>> template = Template("My name is {{ my_name }}.")

>>> context = Context({"my_name": "Adrian"})
>>> template.render(context)
"My name is Adrian."

>>> context = Context({"my_name": "Dolores"})
>>> template.render(context)
"My name is Dolores."

Переменные и поиски

Имена переменных должны состоять из любых букв (A-Z), цифр (0-9), символа подчеркивания (но не должны начинаться с него) или точки.

Точки имеют особое значение в процессе рендеринга шаблона. Точка в имени переменной означает поиск. В частности, когда система шаблонов встречает точку в имени переменной, она выполняет следующие поиски в указанном порядке:

  • Поиск в словаре. Пример: foo["bar"]
  • Поиск атрибута. Пример: foo.bar
  • Поиск индекса в списке. Пример: foo[bar]

Обратите внимание, что «bar» в выражении шаблона, например, {{ foo.bar }} будет интерпретироваться как буквальная строка, а не как значение переменной «bar», если она существует в контексте шаблона.

Система шаблонов использует первый тип поиска, который работает. Это логика короткого замыкания. Вот несколько примеров:

>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."

>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."

>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."

Если какая-либо часть переменной является вызываемым объектом, система шаблонов попытается вызвать её. Пример:

>>> class PersonClass2:
...     def name(self):
...         return "Samantha"
>>> t = Template("My name is {{ person.name }}.")
>>> t.render(Context({"person": PersonClass2}))
"My name is Samantha."

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

  • Если при вызове переменной возникает исключение, исключение будет распространено, если у исключения нет атрибута silent_variable_failure, значение которого равно True. Если исключение *имеет* атрибут silent_variable_failure, значение которого равно True, переменная будет отображаться как значение конфигурации движка string_if_invalid (по умолчанию пустая строка). Пример:

    >>> t = Template("My name is {{ person.first_name }}.")
    >>> class PersonClass3:
    ...     def first_name(self):
    ...         raise AssertionError("foo")
    >>> p = PersonClass3()
    >>> t.render(Context({"person": p}))
    Traceback (most recent call last):
    ...
    AssertionError: foo
    
    >>> class SilentAssertionError(Exception):
    ...     silent_variable_failure = True
    >>> class PersonClass4:
    ...     def first_name(self):
    ...         raise SilentAssertionError
    >>> p = PersonClass4()
    >>> t.render(Context({"person": p}))
    "My name is ."
    

    Обратите внимание, что django.core.exceptions.ObjectDoesNotExist, базовый класс всех исключений API базы данных Django, имеет атрибут silent_variable_failure = True. Поэтому, если вы используете Django-шаблоны с объектами Django-моделей, любое исключение DoesNotExist будет проигнорировано.

  • Переменная может быть вызвана только если у неё нет обязательных аргументов. В противном случае система вернёт значение конфигурации движка string_if_invalid.
  • При вызове некоторых переменных могут быть побочные эффекты, и было бы неразумно или небезопасно допускать доступ системы шаблонов к ним.

    Хороший пример — метод delete() каждого объекта Django-модели. Система шаблонов не должна иметь возможность сделать что-то подобное:

    I will now delete this valuable data. {{ data.delete }}
    

    Для предотвращения этого, установите атрибут alters_data на вызываемой переменной. Система шаблонов не будет вызывать переменную, если у неё установлен атрибут alters_data=True, и вместо этого заменит переменную на string_if_invalid, безусловно. Динамически генерируемые методы delete() и save() объектов Django-моделей получают атрибут alters_data=True автоматически. Пример:

    def sensitive_function(self):
        self.database_record.delete()
    sensitive_function.alters_data = True
    
  • Иногда вам может потребоваться отключить эту функцию по другим причинам и указать системе шаблонов не вызывать переменную независимо от обстоятельств. Для этого установите атрибут do_not_call_in_templates на вызываемой переменной со значением True. Система шаблонов затем будет действовать так, как если бы ваша переменная не была вызываемой (позволяя вам получать доступ к атрибутам вызываемого объекта, например).

Обработка недопустимых переменных шаблонов

В общем случае, если переменная не существует, система шаблонов вставляет значение конфигурации движка string_if_invalid, которое по умолчанию равно '' (пустая строка).

Фильтры, применяемые к недопустимой переменной, будут применены только если string_if_invalid установлено в '' (пустую строку). Если string_if_invalid установлено в какое-либо другое значение, фильтры переменных будут проигнорированы.

Это поведение немного отличается для тегов шаблонов if, for и regroup. Если недопустимая переменная передаётся одному из этих тегов, переменная будет интерпретироваться как None. Фильтры всегда применяются к недопустимым переменным внутри этих тегов.

Если string_if_invalid содержит '%s', маркер формата будет заменён именем недопустимой переменной.

Только для отладки!

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

Многие шаблоны, включая некоторые шаблоны Django, полагаются на молчание системы шаблонов при обнаружении несуществующей переменной. Если вы назначите другое значение, кроме '' для string_if_invalid, у вас возникнут проблемы с рендерингом этих шаблонов и сайтов.

В целом, string_if_invalid следует включать только для отладки конкретной проблемы с шаблоном, а затем отключать после завершения отладки.

Встроенные переменные

Каждый контекст содержит True, False и None. Как и ожидалось, эти переменные разрешаются до соответствующих объектов Python.

Ограничения при работе со строковыми литералами

Язык шаблонов Django не имеет возможности экранировать символы, используемые для собственной синтаксической разметки. Например, тег templatetag необходим, если вам нужно вывести последовательности символов, такие как {% и %}.

Аналогичная проблема возникает, если вы хотите включить эти последовательности в аргументы фильтров или тегов шаблонов. Например, при анализе тега блока Django-парсер шаблона ищет первое вхождение %} после {%. Это предотвращает использование "%}" в качестве строкового литерала. Например, будет выброшено исключение TemplateSyntaxError для следующих выражений:

{% include "template.html" tvar="Some string literal with %} in it." %}

{% with tvar="Some string literal with %} in it." %}{% endwith %}

Та же проблема может возникнуть при использовании зарезервированной последовательности в аргументах фильтра:

{{ some.variable|default:"}}" }}

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

Разбираемся с объектами Context

Большинство времени вы создаёте объекты Context путём передачи заполненного словаря методу Context(). Но вы также можете добавлять и удалять элементы из объекта Context после его создания, используя стандартную синтаксическую запись словарей:

>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
Traceback (most recent call last):
...
KeyError: 'foo'
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'
Context.get(key, otherwise=None)

Возвращает значение для key если key есть в контексте, иначе возвращает otherwise.

Context.setdefault(key, default=None)

Если key присутствует в контексте, возвращает его значение. В противном случае вставляет key со значением default и возвращает default.

Context.pop()
Context.push()
exception ContextPopException

Объект Context представляет собой стек. То есть вы можете push() и pop() его. Если вы pop() слишком много, будет выброшено исключение django.template.ContextPopException:

>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.push()
{}
>>> c['foo'] = 'second level'
>>> c['foo']
'second level'
>>> c.pop()
{'foo': 'second level'}
>>> c['foo']
'first level'
>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
ContextPopException

Также вы можете использовать push() как контекстный менеджер, чтобы гарантировать вызов соответствующего pop().

>>> c = Context()
>>> c['foo'] = 'first level'
>>> with c.push():
...     c['foo'] = 'second level'
...     c['foo']
'second level'
>>> c['foo']
'first level'

Все аргументы, переданные в push() будут переданы конструктору dict для создания нового уровня контекста.

>>> c = Context()
>>> c['foo'] = 'first level'
>>> with c.push(foo='second level'):
...     c['foo']
'second level'
>>> c['foo']
'first level'
Context.update(other_dict)

Помимо push() и pop(), объект Context также определяет метод update(). Он работает как push(), но принимает словарь в качестве аргумента и помещает этот словарь в стек вместо пустого.

>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.update({'foo': 'updated'})
{'foo': 'updated'}
>>> c['foo']
'updated'
>>> c.pop()
{'foo': 'updated'}
>>> c['foo']
'first level'

Как и push(), вы можете использовать update() как контекстный менеджер, чтобы гарантировать вызов соответствующего pop().

>>> c = Context()
>>> c['foo'] = 'first level'
>>> with c.update({'foo': 'second level'}):
...     c['foo']
'second level'
>>> c['foo']
'first level'

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

Context.flatten()

Используя метод flatten(), вы можете получить весь стек Context в виде одного словаря, включая встроенные переменные.

>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.update({'bar': 'second level'})
{'bar': 'second level'}
>>> c.flatten()
{'True': True, 'None': None, 'foo': 'first level', 'False': False, 'bar': 'second level'}

Метод flatten() также используется во внутреннем представлении для сравнения объектов Context.

>>> c1 = Context()
>>> c1['foo'] = 'first level'
>>> c1['bar'] = 'second level'
>>> c2 = Context()
>>> c2.update({'bar': 'second level', 'foo': 'first level'})
{'foo': 'first level', 'bar': 'second level'}
>>> c1 == c2
True

Результат от flatten() может быть полезен в модульных тестах для сравнения Context с dict:

class ContextTest(unittest.TestCase):
    def test_against_dictionary(self):
        c1 = Context()
        c1['update'] = 'value'
        self.assertEqual(c1.flatten(), {
            'True': True,
            'None': None,
            'False': False,
            'update': 'value',
        })

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

class RequestContext(request, dict_=None, processors=None)

Django поставляется со специальным классом Context, django.template.RequestContext, который ведет себя немного иначе, чем обычный django.template.Context. Первое отличие состоит в том, что он принимает объект HttpRequest в качестве первого аргумента. Например:

c = RequestContext(request, {
    'foo': 'bar',
})

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

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

[
    'django.template.context_processors.debug',
    'django.template.context_processors.request',
    'django.contrib.auth.context_processors.auth',
    'django.contrib.messages.context_processors.messages',
]

В дополнение к этому, RequestContext всегда включает 'django.template.context_processors.csrf'. Это обработчик контекста, связанный с безопасностью, необходимый для админской панели и других приложений contrib, и, в случае случайной неправильной настройки, он намеренно встроен и не может быть отключен в параметре context_processors.

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

Когда применяются обработчики контекста

Обработчики контекста применяются поверх данных контекста. Это означает, что обработчик контекста может перезаписывать переменные, которые вы предоставили в своем Context или RequestContext, поэтому будьте внимательны, чтобы избежать имен переменных, которые совпадают с переменными, предоставляемыми обработчиками контекста.

Если вы хотите, чтобы данные контекста имели приоритет над обработчиками контекста, используйте следующий шаблон:

from django.template import RequestContext

request_context = RequestContext(request)
request_context.push({"my_name": "Adrian"})

Django делает это, чтобы позволить данным контекста переопределять обработчики контекста в API, таких как render() и TemplateResponse.

Также вы можете предоставить RequestContext список дополнительных обработчиков, используя необязательный третий позиционный аргумент, processors. В этом примере экземпляр RequestContext получает переменную ip_address:

from django.http import HttpResponse
from django.template import RequestContext, Template

def ip_address_processor(request):
    return {'ip_address': request.META['REMOTE_ADDR']}

def client_ip_view(request):
    template = Template('{{ title }}: {{ ip_address }}')
    context = RequestContext(request, {
        'title': 'Your IP Address',
    }, [ip_address_processor])
    return HttpResponse(template.render(context))

Встроенные обработчики контекста шаблонов

Вот что делает каждый из встроенных обработчиков:

django.contrib.auth.context_processors.auth

auth(request)

Если этот обработчик включен, каждый RequestContext будет содержать следующие переменные:

  • user – экземпляр auth.User, представляющий текущего пользователя (или экземпляр AnonymousUser , если клиент не авторизован).
  • perms – экземпляр django.contrib.auth.context_processors.PermWrapper, представляющий разрешения текущего пользователя.

django.template.context_processors.debug

debug(request)

Если этот обработчик включен, каждый RequestContext будет содержать две эти переменные – только если ваш параметр DEBUG установлен в True и адрес IP запроса (request.META['REMOTE_ADDR']) находится в настройке INTERNAL_IPS:

  • debug – True. Вы можете использовать его в шаблонах, чтобы проверить, находитесь ли вы в режиме DEBUG.
  • sql_queries – список словарей {'sql': ..., 'time': ...}, представляющих каждую SQL-запрос, которая произошла во время запроса, и сколько времени на неё потребовалось. Список упорядочен по имени базы данных, а затем по запросу. Он генерируется по запросу.

django.template.context_processors.i18n

i18n(request)

Если этот обработчик включен, каждый RequestContext будет содержать следующие переменные:

  • LANGUAGES – значение настройки LANGUAGES.
  • LANGUAGE_BIDI – True, если текущий язык – язык справа налево, например иврит или арабский. False , если язык слева направо, например английский, французский или немецкий.
  • LANGUAGE_CODE – request.LANGUAGE_CODE, если он существует. В противном случае значение параметра LANGUAGE_CODE.

См. i18n теги шаблонов для тегов шаблонов, которые генерируют те же значения.

django.template.context_processors.media

Если этот обработчик включен, каждый RequestContext будет содержать переменную MEDIA_URL, предоставляющую значение настройки MEDIA_URL.

django.template.context_processors.static

static(request)

Если этот обработчик включен, каждый RequestContext будет содержать переменную STATIC_URL, предоставляющую значение настройки STATIC_URL.

django.template.context_processors.csrf

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

django.template.context_processors.request

Если этот обработчик включен, каждый RequestContext будет содержать переменную request, которая представляет собой текущий объект HttpRequest.

django.template.context_processors.tz

tz(request)

Если этот процессор включён, каждый RequestContext будет содержать переменную TIME_ZONE, предоставляющую имя текущей активной часовой зоны.

django.contrib.messages.context_processors.messages

Если этот процессор включён, каждый RequestContext будет содержать эти две переменные:

  • messages – Список сообщений (в виде строк), установленных через фреймворк сообщений.
  • DEFAULT_MESSAGE_LEVELS – Сопоставление имён уровней сообщений с их числовыми значениями.

Создание собственных процессоров контекста

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

Например, чтобы добавить настройку DEFAULT_FROM_EMAIL в каждый контекст:

from django.conf import settings

def from_email(request):
    return {
        "DEFAULT_FROM_EMAIL": settings.DEFAULT_FROM_EMAIL,
    }

Пользовательские процессоры контекста могут быть расположены где угодно в вашем коде. Всё, что нужно Django, — это то, что ваши пользовательские процессоры контекста указываются параметром 'context_processors' в вашей настройке TEMPLATES — или аргументом context_processors метода Engine, если вы используете его напрямую.

Загрузка шаблонов

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

Django ищет каталоги шаблонов в ряде мест, в зависимости от настроек загрузки шаблонов (см. «Типы загрузчиков» ниже), но самый простой способ указать каталоги шаблонов — использовать параметр DIRS.

Параметр DIRS

Укажите Django ваши каталоги шаблонов, используя параметр DIRS в настройке TEMPLATES в вашем файле настроек — или аргумент dirs метода Engine. Это должно быть задано как список строк, содержащих полные пути к вашим каталогам шаблонов:

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

Шаблоны могут находиться где угодно, при условии, что каталоги и шаблоны читаемы веб-сервером. Они могут иметь любое расширение, например .html или .txt, или они могут вообще не иметь расширения.

Обратите внимание, что эти пути должны использовать слеши Unix-стиля, даже в Windows.

Типы загрузчиков

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

Некоторые из этих других загрузчиков по умолчанию отключены, но вы можете активировать их, добавив параметр 'loaders' в ваш бэкэнд DjangoTemplates в настройке TEMPLATES или передав аргумент loaders методу Engine. loaders должен быть списком строк или кортежей, где каждый элемент представляет класс загрузчика шаблонов. Вот загрузчики шаблонов, которые поставляются с Django:

django.template.loaders.filesystem.Loader

class filesystem.Loader

Загружает шаблоны с файловой системы в соответствии с DIRS.

Этот загрузчик включён по умолчанию. Однако он не найдёт шаблонов, пока вы не зададите DIRS непустым списком:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],
}]

Вы также можете переопределить 'DIRS' и указать определённые каталоги для конкретного загрузчика файловой системы:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'OPTIONS': {
        'loaders': [
            (
                'django.template.loaders.filesystem.Loader',
                [BASE_DIR / 'templates'],
            ),
        ],
    },
}]

django.template.loaders.app_directories.Loader

class app_directories.Loader

Загружает шаблоны из приложений Django с файловой системы. Для каждого приложения в INSTALLED_APPS загрузчик ищет подкаталог templates. Если каталог существует, Django ищет шаблоны в нём.

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

Например, для этой настройки:

INSTALLED_APPS = ['myproject.polls', 'myproject.music']

…тогда get_template('foo.html') будет искать foo.html в этих каталогах в указанном порядке:

  • /path/to/myproject/polls/templates/
  • /path/to/myproject/music/templates/

… и будет использовать тот, который найдёт первым.

Порядок следования в INSTALLED_APPS имеет значение! Например, если вы хотите настроить административную панель Django, вы можете переопределить стандартный шаблон admin/base_site.html из django.contrib.admin своим собственным admin/base_site.html в myproject.polls. После этого вам нужно убедиться, что ваше приложение myproject.polls стоит перед django.contrib.admin в INSTALLED_APPS, иначе шаблоны django.contrib.admin будут загружены первыми, а ваши будут проигнорированы.

Обратите внимание, что загрузчик выполняет оптимизацию при первом запуске: он кеширует список пакетов INSTALLED_APPS, у которых есть подкаталог templates.

Вы можете включить этот загрузчик, установив APP_DIRS в True:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'APP_DIRS': True,
}]

django.template.loaders.cached.Loader

class cached.Loader

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

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

Этот загрузчик автоматически включен, если OPTIONS['loaders'] не указан.

Вы можете вручную указать кеширование шаблонов с помощью некоторых пользовательских загрузчиков шаблонов, используя такие настройки:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.cached.Loader', [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
                'path.to.custom.Loader',
            ]),
        ],
    },
}]

Примечание

Все встроенные теги шаблонов Django безопасны для использования с кешированным загрузчиком, но если вы используете пользовательские теги шаблонов, которые взяты из пакетов сторонних разработчиков или написаны вами, вы должны убедиться, что реализация Node для каждого тега является потокобезопасной. Более подробную информацию см. в разъяснении потоковой безопасности тега шаблона.

Изменено в Django 4.1:

Кешированный загрузчик шаблонов был включён всякий раз, когда OPTIONS['loaders'] не был указан. Ранее он включался только тогда, когда DEBUG был False.

django.template.loaders.locmem.Loader

class locmem.Loader

Загружает шаблоны из словаря Python. Это полезно для тестирования.

Этот загрузчик принимает словарь шаблонов в качестве первого аргумента:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.locmem.Loader', {
                'index.html': 'content here',
            }),
        ],
    },
}]

Этот загрузчик отключён по умолчанию.

Django использует загрузчики шаблонов в порядке, указанном в параметре 'loaders' . Он использует каждый загрузчик до тех пор, пока загрузчик не найдёт совпадение.

Пользовательские загрузчики

Можно загружать шаблоны из дополнительных источников с помощью пользовательских загрузчиков шаблонов. Пользовательские классы Loader должны наследоваться от django.template.loaders.base.Loader и определять методы get_contents() и get_template_sources().

Методы загрузчика

class Loader

Загружает шаблоны из заданного источника, такого как файловая система или база данных.

get_template_sources(template_name)

Метод, принимающий template_name и возвращающий Origin экземпляры для каждого возможного источника.

Например, загрузчик файловой системы может получить 'index.html' в качестве template_name аргумента. Этот метод вернёт происхождения полного пути index.html по мере его появления в каждом каталоге шаблонов, рассматриваемом загрузчиком.

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

get_contents(origin)

Возвращает содержимое шаблона, заданного экземпляром Origin.

Здесь загрузчик файловой системы считывает содержимое из файловой системы, а загрузчик базы данных — из базы данных. Если соответствующий шаблон не существует, должно быть возбуждено исключение TemplateDoesNotExist.

get_template(template_name, skip=None)

Возвращает объект Template для данного template_name, проходя по результатам get_template_sources() и вызывая get_contents(). Возвращается первый совпавший шаблон. Если шаблон не найден, возбуждается исключение TemplateDoesNotExist.

Необязательный аргумент skip — список происхождений, которые следует игнорировать при расширении шаблонов. Это позволяет шаблонам расширять другие шаблоны с тем же именем. Также это используется для предотвращения ошибок рекурсии.

В целом, достаточно определить get_template_sources() и get_contents() для пользовательских загрузчиков шаблонов. get_template() обычно переопределять не нужно.

Разработка собственного

Примеры можно найти в исходном коде встроенных загрузчиков Django.

Происхождение шаблона

Шаблоны имеют origin содержащий атрибуты, зависящие от источника, из которого они загружены.

class Origin(name, template_name=None, loader=None)
name

Путь к шаблону, возвращаемый загрузчиком шаблонов. Для загрузчиков, которые считывают из файловой системы, это полный путь к шаблону.

Если шаблон создается напрямую, а не через загрузчик шаблонов, это строковое значение <unknown_source>.

template_name

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

Если шаблон создается напрямую, а не через загрузчик шаблонов, это None.

loader

Экземпляр загрузчика шаблонов, создавший этот Origin.

Если шаблон создается напрямую, а не через загрузчик шаблонов, это None.

django.template.loaders.cached.Loader требует, чтобы все его обернутые загрузчики устанавливали этот атрибут, обычно создавая Origin с loader=self.

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

Spec-Zone.ru

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