Использование системы шаблонов Django
Сейчас мы приступим к изучению системы шаблонов, но пока не станем интегрировать ее с созданными в предыдущей главе представлениями. Наша цель — показать, как работает система шаблонов вне связи с другими частями фреймворка Django. (Обычно шаблоны используются совместно с представлениями, но мы хотим, чтобы вы поняли, что система шаблонов - просто библиотека на Python, применимая повсюду, а не только в представлениях Django.)
Вот как выглядит самый простой способ использования системы шаблонов Django в коде на Python:
1. Создаем объект Template, передав код шаблона в виде строки.
2. Вызываем метод render() объекта Template с заданным набором переменных (;контекстом). Метод возвращает результат обработки шаблона в виде строки, в которой все переменные и шаблонные теги вычислены согласно переданному контексту.
В коде это выглядит следующим образом:
»> from django import template
»> t = template. TemplateC Меня зовут {{ name }}.’)
»> с = template. Context({‘ name’ : ‘Адриан’})
»> print t. render(c)
Меня зовут Адриан.
»> с = template. Context ({4 name’ : ‘Фред’}) »> print t. render(c) Меня зовут Фред.
В следующих разделах эти шаги описываются гораздо более подробно.
Создание объектов Template
Проще всего создать объект Template непосредственно. Класс Template находится в модуле django.template, его конструктор принимает единственный аргумент - код шаблона. Запустим интерактивный интерпретатор Python и посмотрим, как это работает в конкретной программе.
Находясь в каталоге проекта mysite, созданного командой django-admin. ру startproject (см. главу 2), выполните команду python manage.ру shell, чтобы войти в интерактивный интерпретатор.
Специальное приглашение Python
Если вы работали с Python раньше, то может возникнуть вопрос, почему нужно запускать оболочку командой python manage, ру shell, а не просто python. Та и другая запускают интерактивный интерпретатор, но у команды manage, ру shell есть одно важное отличие: до запуска интерпретатора она сообщает Django о том, какой файл параметров следует использовать. От этих параметров зависят многие части фреймворка Django, в том числе система шаблонов, и, чтобы ими можно было воспользоваться, фреймворк должен знать, откуда взять параметры.
Для тех, кому интересно, расскажем, что происходит за кулисами. Django ищет переменную окружения DJANGO_SETTINGS_MODULE, значением которой должен быть путь импорта для файла settings, ру. Например, DJANGO_SETTINGS_MODULE может быть равна ‘mysite. settings’ в предположении, что mysite включен в путь Python.
Если оболочка запускается командой python manage, ру shell, то она берет на себя все хлопоты по установке значения переменной DJANGO_SETTINGS_MODULE. Мы рекомендуем во всех примерах использовать команду manage.ру shell, чтобы избавить себя от необходимости самостоятельно задавать конфигурацию.
Когда вы получше освоитесь с Django, то, наверное, перестанете использовать команду manage.ру shell и будете устанавливать переменную DJANGO_SETTINGS_MODULE вручную в своем файле .bash_ profile или каком-нибудь другом конфигурационном файле системной оболочки.
А теперь перейдем к основам системы шаблонов.
»> from django. template Import Template »> t = Template(‘My name is {{ name }}.’) »> print t
Выполнив эти команды в интерактивном интерпретаторе, вы увидите примерно такое сообщение:
<django.template.Template object at 0xb7d5f24c>
Вместо 0xb7d5f24c каждый раз будет печататься новое значение, но оно несущественно (это просто внутренний идентификатор объекта Template, если вам интересно).
При создании объекта Template система компилирует исходный код шаблона во внутреннюю оптимизированную форму, пригодную для отображения. Но если исходный код шаблона содержит ошибки, то обращение к конструктору Template() возбудит исключение TemplateSyntaxError:
»> from django. template import Template »> t = Template(‘ {% notatag %}’) Traceback (most recent call last): File "<stdin>", line 1, in ?
django.template.TemplateSyntaxError: Invalid block tag: ‘notatag’
Слова block tag (блочный тег) относятся к тегу {% notatag %}. Выражения блочный тег и шаблонный тег - синонимы.
Система возбуждает исключение TemplateSyntaxError в следующих случаях:
• Недопустимый тег
• Недопустимые аргументы допустимого тега
• Недопустимый фильтр
• Недопустимые аргументы допустимого фильтра
• Недопустимый синтаксис шаблона
• Незакрытые теги (если требуется наличие закрывающего тега)
Отображение шаблона
Объекту Template можно передать данные в виде контекста. Контекст - это просто набор именованных переменных шаблона вместе с их значениями. Шаблон использует контекст, чтобы инициализировать свои переменные и вычислить теги.
В Django контекст представляется классом Context, который находится в модуле django.template. Его конструктор принимает один необязательный параметр - словарь, отображающий имена переменных в их значения. Вызовем метод render() объекта Template, передав ему контекст для «заполнения» шаблона:
»> from django.template import Context, Template »> t = Template(‘ Меня зовут {{ name }}.’) »> с = Context({‘name’: ‘Стефан’}) »> t. render(c) и’Меня зовут Стефан.’
Подчеркнем, что метод t. render(c) возвращает объект Unicode, а не обычную строку Python. Это видно по наличию буквы и перед строкой. Объекты Unicode используются вместо строк повсюду в фреймворке Django. Если вы понимаете, к каким последствиям это приводит, то благодарите Django за все те ухищрения, на которые он идет, чтобы все работало правильно. А если не понимаете, то и не забивайте себе голову; просто помните, что поддержка Unicode в Django позволяет приложениям относительно безболезненно работать с разнообразными наборами символов, помимо обычной латиницы.
Словари и контексты
В языке Python словарем называется отображение между ключами и значениями. Объект Context похож на словарь, но обладает дополнительной функциональностью, которая рассматривается в главе 9.
Имя переменной должно начинаться с буквы (A-Z или a-z) и может содержать буквы, цифры, знаки подчеркивания и точки. (Точки - это особый случай, который мы обсудим в разделе «Поиск контекстных переменных».) Строчные и заглавные буквы в именах переменных считаются различными.
Ниже показан пример компиляции и отображения шаблона, очень похожего на тот, что приведен в начале главы.
»> from django.template import Template, Context »> raw_template = <р>Уважаемый(ая) {{ person_name }},</p>
… <р>Спасибо, что вы сделали заказ в {{ company }}. Он будет … доставлен вам {{ ship_date|date:"F j, Y" }}.</p>
… {% if ordered_warranty %}
… <p> Сведения о гарантийных обязательствах вы найдете внутри упаковки.</р> . . . {% else %}
. . . <р> Вы не заказывали гарантию, поэтому должны будете действовать
самостоятельно, когда изделие неизбежно выйдет из строя.</р> . . . {% endif %}
… <р>С уважением,<Ьг />{{ company }}</р>……..
»> t = Template(raw_template) »> import datetime
»> с = Context( {‘ person_name’: ‘Джон Смит’, ‘company’: ‘Outdoor Equipment’, ‘ship_date’: datetime.date(2009, 4, 2), ‘ordered_warranty’: False}) »> t. render(c)
u"<p> Уважаемый(ая) Джон Смит,</p>\n\n<p> Спасибо, что вы сделали заказ в Outdoor Equipment. Он будет\п доставлен вам April 2, 2009.</p>\n\n\n<p> Вы не заказывали гарантию, поэтому должны будете действовать \п самостоятельно, когда изделие неизбежно выйдет из строя.</р>\п\п\п<р>
С уважением,<br />0utdoor Equipment </р>"
Рассмотрим этот код по частям:
1. Сначала из модуля django.template импортируются классы Template и Context.
2. Исходный текст шаблона мы сохраняем в переменной rawjtemplate. Обратите внимание, что строка начинается и завершается тремя знаками кавычек, так как продолжается в нескольких строчках; строки, ограниченные с двух сторон одним знаком кавычки, не могут занимать несколько строчек.
3. Далее мы создаем объект шаблона t, передавая rawjtemplate конструктору класса Template.
4. Импортируем модуль datetime из стандартной библиотеки Python, поскольку он понадобится в следующей инструкции.
5. Создаем объект с класса Context. Конструктор класса Context принимает словарь Python, который отображает имена переменных в их значения. Так, мы указываем, что переменная person_name содержит значение ‘Джон Смит’, переменная company - значение ‘Outdoor Equipment’ и так далее.
6. Наконец, мы вызываем метод render(), передавая ему контекст. Он возвращает результат отображения шаблона, в котором шаблонные переменные заменены фактическими значениями и обработаны все шаблонные теги.
Отметим, что выведен абзац «Вы не заказывали гарантию», поскольку переменная ordered_warranty равна False. Также отметим, что дата April 2, 2024 отформатирована в соответствии с форматом ‘F j, У. (Спецификаторы формата для фильтра date описываются в приложении Е.)
У человека, не знакомого с языком Python, может возникнуть вопрос, почему, вместо того, чтобы просто разрывать строки, мы выводим символы перевода строки (‘\п’). Это объясняется одной тонкостью в интерактивном интерпретаторе Python: вызов метода t.render(c) возвращает строку, а интерактивный интерпретатор по умолчанию выводит представление строки, а не ее истинное значение. Если вы хотите увидеть, где разрываются строки, то воспользуйтесь инструкцией print: print t.render(c).
Вот вы и познакомились с основами использования системы шаблонов в Django: пишем исходный код шаблона, создаем объект Template, создаем объект Context и вызываем метод render().
Один шаблон, несколько контекстов
Существующий объект Template можно использовать для отображения нескольких контекстов. Рассмотрим следующий пример:
>» from django.template import Template, Context »> t = Template(‘ Привет, {{ name }}’) »> print t. render(Context({‘ name’ : ‘Джон’})) Привет, Джон
»> print t. render(Context( {‘ name’: ‘Джулия’})) Привет, Джулия
»> print t. render(Context( {‘ name’: ‘Пэт’})) Привет, Пэт
Когда один и тот же исходный шаблон необходимо использовать для отображения нескольких контекстов, эффективнее создать один объект Template и несколько раз вызвать его метод render():
# Так плохо
for name in (‘Джон’, ‘Джулия’, ‘Пэт’): t = Template(‘Hello, {{ name }}’) print t.render(Context({‘name’: name}))
# А так хорошо
t = Template(‘Hello, {{ name }}’) for name in (‘Джон’, ‘Джулия’, ‘Пэт’):
print t.render(Context({‘name’: name}))
Синтаксический разбор шаблонов в Django производится очень быстро. По большей части разбор сводится к сопоставлению с одним регулярным выражением. Это составляет разительный контраст с системами шаблонов на базе XML, которые из-за больших накладных расходов на работу анализатора XML оказываются на несколько порядков медленнее механизма отображения шаблонов в Django.
Поиск контекстных переменных
В приведенных выше примерах мы передавали в контексте простые переменные - в основном строки да еще дату. Однако система шаблонов способна элегантно обрабатывать и более сложные структуры данных - списки, словари и пользовательские объекты.
Ключом к обработке сложных структур данных в шаблонах Django является знак точки (.). Точка позволяет получить доступ к словарю по ключу, к элементам списка по индексу, а также к атрибутам и методам объекта.
Лучше всего проиллюстрировать ее использование на примерах. Пусть, например, мы передаем в шаблон словарь Python. Чтобы получить доступ к хранящимся в словаре значениям по ключу, воспользуемся точкой:
»> from django.template import Template, Context »> person = {‘name’: ‘ Sally’, ‘age’: ’43’}
»> t = Template(‘{{ person.name }} is {{ person.age }} years old.’) »> с = Context({‘person’: person}) »> t. render(c) u’Sally is 43 years old.’
Точка позволяет также обращаться к атрибутам объекта. Например, у объекта datetime. date имеются атрибуты year, month и day, и для доступа к ним из шаблона Django можно воспользоваться точкой, как показано ниже:
»> from django.template Import Template, Context
»> import datetime
»> d = datetime.date(1993, 5, 2)
»> d.year
1993
»> d.month 5
»> d.day 2
»> t = TemplateC Месяц равен {{ date.month }}, а год равен {{ date.year ».’)
»> с = Context({‘date’ : d}) »> t. render(c)
u’Месяц равен 5, а год равен 1993.’
Далее на примере пользовательского класса демонстрируется, как с помощью точки можно обращаться к атрибутам произвольных объектов:
»> from django.template import Template, Context »> class Person(object):
def___________________ init__ (self, first_name, last_name):
self.first_name, self.last_name = first_name, last_name »> t = Template(‘Привет, {{ person.first_name }} {{ person.last_name }}.’) »> с = Context( {‘ person’ : Person(‘ Джон’, ‘Смит’)}) »> t. render(c) u’Привет, Джон Смит.’
С помощью точки можно также вызывать методы объектов. Например, у любой строки Python есть методы upper() и isdigit(), и к ним можно обратиться из шаблона Django с помощью точно такого же синтаксиса:
»> from django.template import Template, Context
»> t = Template(‘{{ var }}-{{ var.upper }}-{{ var.isdigit }}’)
»> t. render(Context({‘var’: ‘hello’}))
u’hello-HELLO-False’
»> t. render(Context({‘var’: ‘123’}))
u’123-123-True’
Отметим, что при вызове методов скобки опускаются. Кроме того, методам невозможно передать аргументы; вызывать разрешается только методы без обязательных аргументов. (Почему так, мы объясним в следующей главе.)
Наконец, точки применяются для доступа к элементам списка по индексу:
»> from django.template import Template, Context
»> t = TemplateC Элемент 2 - {{ items. 2 }}.’)
»> с = Context({‘items’: [‘яблоки’, ‘бананы’, ‘морковки’]})
»> t. render(c)
u’Элемент 2 - морковки.’
Отрицательные индексы не допускаются. Например, обращение к шаблонной переменной {{ items.-1 }} приведет к исключению TemplateSyntax- Error.
Списки в языке Python
Напомним, что в языке Python нумерация элементов списка начинается с 0. Индекс первого элемента равен 0, второго - 1 и т. д.
Когда система встречает в шаблоне точку в имени переменной, она производит поиск подходящей переменной в следующем порядке:
• Доступ к словарю (например, foo["barM])
• Доступ к атрибуту (например, foo. bar)
• Вызов метода (например, foo.barQ)
• Доступ к списку по индексу (например, f оо[2])
Поиск останавливается, как только будет найдено первое подходящее имя.
Поиск имени может быть и многоуровневым. Например, конструкция {{ person.name.upper }} транслируется в последовательность из двух шагов: сначала производится обращение к словарю (person[ name ]), а затем - вызов метода (иррег()):
»> from django.template import Template, Context »> person = {‘name’: ‘Sally’, ‘age’: ’43’}
»> t = Template(‘{{ person.name.upper }} is {{ person.age }} years old.’)
»> с = Context({‘person’: person})
>>> t.render(c)
u’SALLY is 43 years old.’
Вызовы методов
Вызовы методов несколько сложнее, чем другие виды доступа по точке. Перечислим несколько моментов, которые следует иметь в виду.
Если во время вызова метода возникнет исключение, то оно распространяется вверх по стеку, если в объекте-исключении нет атрибута silent_variable_failure со значением True. Если же такой атрибут имеется, то при отображении переменная заменяется пустой строкой, как в следующем примере:
»> t = Template("MeHfl зовут {{ 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(AssertionError):
silent_variable_failure = True »> class PersonClass4:
def first_name(self):
raise SilentAssertionError »> p = PersonClass4() »> t. render(Context({"person": p})) u’Меня зовут .’
• Вызов метода возможен, только если у метода нет обязательных аргументов. В противном случае система начнет проверять следующий по порядку вид доступа (поиск элемента списка по индексу).
• Очевидно, что у некоторых методов есть побочные действия, и было бы глупо и, быть может, даже небезопасно разрешать системе шаблонов обращаться к ним.
Пусть, например, в классе BankAccount имеется метод delete(). Если в шаблоне встречается конструкция {{ account.delete }}, где account - объект класса BankAccount, то при отображении такого шаблона объект будет удален!
Чтобы предотвратить такое развитие событий, задайте для метода атрибут alters_data:
def delete(self):
# Удаление счета delete.alters_data = True
• Система шаблонов не будет вызывать метод, помеченный таким образом. В приведенном выше примере, если в шаблоне встретится конструкция {{ account.delete }} и для метода deleteO будет задан атрибут alters_data=True, то метод deleteO не будет вызван при отображении. Он просто будет проигнорирован.
Как обрабатываются переменные, отсутствующие в контексте
По умолчанию, если переменная не определена в контексте, в процессе отображения система шаблонов выведет вместо нее пустую строку. Рассмотрим такой пример:
»> from django.template import Template, Context »> t = TemplateC Вас зовут {{ name }}.’) »> t. render(ContextO) u’Вас зовут .’
»> t. render(Context({‘ var’ : ‘hello’})) u’Вас зовут .’
»> t. render(Context({‘NAME’: ‘hello’})) u’Вас зовут .’
»> t. render(Context({‘Name’ : ‘hello’})) u’Вас зовут .’
Система не возбуждает исключения, поскольку спроектирована так, чтобы прощать ошибки пользователя. В данном случае все виды поиска завершаются неудачно, потому что имя переменной задано неправильно или не в том регистре. В действующих приложениях недопустимо, чтобы сайт оказывался недоступен из-за мелкой синтаксической ошибки в шаблоне.
Модификация контекстных объектов
В большинстве случаев при создании объектов Context конструктору передается заполненный словарь. Но разрешается добавлять и удалять элементы в объект Context и после его создания, для чего применяется стандартный синтаксис Python:
»> from django.template import Context »> с = Context({"foo": "bar"}) »> c[‘ foo’ ] ‘ bar’
»> del c[‘ foo’ ]
»> c[1 foo1 ]
Traceback (most recent call last): KeyError: ‘foo’
»> c[‘ newvariable’] = ‘hello’ »> c[‘ newvariable’ ] ‘hello’
Источник: Головатый А., Каплан-Мосс Дж. Django. Подробное руководство, 2-е издание. - Пер. с англ. - СПб.: Символ- Плюс, 2010. - 560 е., ил.