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

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

Она предполагает понимание шаблонов, контекстов, переменных, тегов и рендеринга. Начните с introduction to the Django template language, если вы не знакомы с этими понятиями.

Обзор

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

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

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

  1. Для каждого бэкенда DjangoTemplates в настройках TEMPLATES, Django создает Engine. DjangoTemplates оборачивает Engine и адаптирует его к общему API бэкенда шаблона.
  2. Модуль django.template.loader предоставляет такие функции, как get_template() для загрузки шаблонов. Они возвращают django.template.backends.django.Template, который обертывает фактический django.template.Template.
  3. Полученный на предыдущем шаге 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-путей к вызываемым файлам, которые используются для заполнения контекста, когда шаблон отображается вместе с запросом. Эти callables принимают объект запроса в качестве аргумента и возвращают dict элементов, которые должны быть объединены в контекст.

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

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

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

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

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

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

    • 'django.template.loaders.filesystem.Loader'
    • 'django.template.loaders.app_directories.Loader' тогда и только тогда, когда app_dirs является True.

    Если debug равно False, то эти загрузчики обернуты в django.template.loaders.cached.Loader.

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

  • 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 к модулям тегов шаблона, которые нужно добавить в built-ins. Например:

    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 принимает необязательный аргумент - словарь, отображающий имена переменных на их значения.

Подробнее см. ниже Игра с объектами Context.

Template.render(context)[исходный код]

Вызовите метод Template объекта render() с 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 DoesNotExist, имеет 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 необходим, если вам нужно вывести последовательности символов типа {% и %}.

Аналогичная проблема возникает, если вы хотите включить эти последовательности в фильтр шаблона или аргументы тегов. Например, при разборе тега block, анализатор шаблонов 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 в качестве стека пригодится в some custom template tags.

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'. Это контекстный процессор, связанный с безопасностью, необходимый администратору и другим приложениям, и, на случай случай случайной неправильной конфигурации, он намеренно жестко закодирован и не может быть отключен в опции 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()[исходный код]

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

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

django.template.context_processors.debug

debug()[исходный код]

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

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

django.template.context_processors.i18n

i18n()[исходный код]

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

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

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

django.template.context_processors.media

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

django.template.context_processors.static

static()[исходный код]

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

django.template.context_processors.csrf

Этот процессор добавляет маркер, который необходим тегу шаблона csrf_token для защиты от Cross Site Request Forgeries.

django.template.context_processors.request

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

django.template.context_processors.tz

tz()[исходный код]

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

django.contrib.messages.context_processors.messages

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

  • messages – Список сообщений (в виде строк), которые были заданы через messages framework.
  • DEFAULT_MESSAGE_LEVELS – Сопоставление имен уровней сообщений с their numeric value.

Написание собственных контекстных процессоров

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

Пользовательские контекстные процессоры могут находиться где угодно в вашей кодовой базе. Все, о чем заботится 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

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

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

Этот загрузчик автоматически включается, если не указано OPTIONS['loaders'] и OPTIONS['debug'] равно False (последний параметр по умолчанию имеет значение DEBUG).

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

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 для каждого тега является потокобезопасной. Для получения дополнительной информации см. раздел template tag thread safety considerations.

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() обычно не нужно переопределять.

Постройте свой собственный

В качестве примера можно привести source code for Django’s built-in loaders.

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

Шаблоны имеют 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.

Вернуться на верх