Django_-_podrobnoe_rukovodstvo
.pdf4
Шаблоны
Возможно, вас удивил способ, которым мы возвращали текст в примерах представлений из предыдущей главы. Мы встраивали HTML-раз метку прямо в код на Python, например:
def current_datetime(request): now = datetime.datetime.now()
html = “<html><body>Сейчас %s.</body></html>” % now return HttpResponse(html)
Хотя это удобно, когда нужно объяснить, как работают представления, но, вообще говоря, «зашивать» HTML-разметку непосредственно в представление – порочная идея. И вот почему.
•• При любом изменении в дизайне страницы потребуется модифицировать код на Python. Но дизайн сайта обычно изменяется гораздо чаще, чем лежащий в его основе код, поэтому хотелось бы иметь возможность изменять дизайн, не трогая код.
•• Программирование на Python и дизайн на HTML – разные виды деятельности, и в большинстве коллективов, профессионально занимающихся веб-разработкой, за них отвечают разные люди (и даже разные подразделения). От дизайнеров и верстальщиков на HTML/ CSS нельзя требовать умения редактировать код на Python.
•• Работа выполняется наиболее эффективно, когда программисты могут трудиться над программным кодом, а дизайнеры – над шаб лонами одновременно, не дожидаясь, пока кто-то закончит редактировать единственный файл, содержащий и код на Python, и разметку.
Поэтому было бы гораздо элегантнее и удобнее для сопровождения отделить дизайн страницы от ее кода на Python. Система шаблонов Django, которую мы обсудим в этой главе, как раз и позволяет это сделать.
Принципы работы системы шаблонов |
61 |
Принципы работы системы шаблонов
Шаблон в Django представляет собой строку текста, предназначенную для отделения представления документа от его данных. В шаблоне могут встречаться маркеры и простые логические конструкции (шаб лонные теги), управляющие отображением документа. Обычно шаб лоны применяются для порождения HTML-разметки, но в Django они позволяют генерировать документы в любом текстовом формате.
Начнем с простого примера. Следующий шаблон Django описывает HTML-страницу, на которой отображается благодарность пользователю, разместившему заказ. Можно считать, что это бланк письма.
<html>
<head><title>Извещение о сделанном заказе</title></head>
<body>
<h1> Извещение о сделанном заказе</h1>
<p>Уважаемый(ая) {{ person_name }}!</p>
<p>Спасибо, что вы сделали заказ в {{ company }}. Он будет доставлен вам {{ ship_date|date:”F j, Y” }}.</p>
<p>Ниже перечислены заказанные вами товары:</p>
<ul>
{% for item in item_list %} <li>{{ item }}</li>
{% endfor %} </ul>
{% if ordered_warranty %}
<p>Сведения о гарантийных обязательствах вы найдете внутри упаковки.</p> {% else %}
<p>Вы не заказывали гарантию, поэтому должны будете действовать самостоятельно, когда изделие неизбежно выйдет из строя.</p>
{% endif %}
<p>С уважением,<br />{{ company }}</p>
</body>
</html>
По существу, этот шаблон представляет собой HTML-разметку, в которую были добавлены переменные и шаблонные теги. Рассмотрим его более подробно.
•• Любой текст, окруженный парой фигурных скобок (например, {{ person_name }}) – это переменная. Такая конструкция означает, что нужно вставить значение переменной с указанным именем. Как задаются значения переменных? Скоро дойдем и до этого.
62 |
Глава 4. Шаблоны |
•• Любой текст внутри фигурных скобок со знаками процента (например, {% if ordered_warranty %}) – шаблонный тег. Определение тега достаточно широкое: тег просто говорит системе, что нужно «сделать нечто».
В данном примере шаблон содержит тег for ({% for item in item_list %}) и тег if ({% if ordered_warranty %}).
Тег for очень похож на инструкцию for языка Python, то есть позволяет выполнить обход всех элементов последовательности. Тег if, как нетрудно догадаться, действует как условная инструкция «if». В данном случае этот тег проверяет, совпадает ли значение переменной ordered_warranty с True. Если да, то система шаблонов выведет весь текст между {% if ordered_warranty %} и {% else %}. Если нет, то будет выведен текст между {% else %} и {% endif %}. Отметим, что ветвь {% else %} необязательна.
•• Во втором абзаце этого шаблона встречается еще и фильтр – самый удобный способ отформатировать переменную. В данном случае он выглядит следующим образом: {{ ship_date|date:”F j, Y” }}. Мы передаем переменную ship_date фильтру date с аргументом “F j, Y”. Фильтр date форматирует даты в соответствии с форматом, заданным в аргументе. Фильтры присоединяются с помощью символа вертикальной черты (|), который служит напоминанием о конвейерах UNIX.
Каждый шаблон Django имеет доступ к нескольким встроенным тегам
ифильтрам, большинство из которых мы обсудим в следующих разделах. В приложении F приведен полный перечень всех тегов и фильтров,
имы рекомендуем ознакомиться с ним, чтобы знать, какие вообще имеются возможности. Можно также создавать собственные теги и фильтры, но об этом мы поговорим в главе 9.
Использование системы шаблонов
Сейчас мы приступим к изучению системы шаблонов, но пока не станем интегрировать ее с созданными в предыдущей главе представлениями. Наша цель – показать, как работает система шаблонов вне связи с другими частями фреймворка Django. (Обычно шаблоны используются совместно с представлениями, но мы хотим, чтобы вы поняли, что сис тема шаблонов – просто библиотека на Python, применимая повсюду, а не только в представлениях Django.)
Вот как выглядит самый простой способ использования системы шаб лонов Django в коде на Python:
1.Создаем объект Template, передав код шаблона в виде строки.
2.Вызываем метод render() объекта Template с заданным набором переменных (контекстом). Метод возвращает результат обработки шаб лона в виде строки, в которой все переменные и шаблонные теги вычислены согласно переданному контексту.
Использование системы шаблонов |
63 |
Вкоде это выглядит следующим образом:
>>>from django import template
>>>t = template.Template(‘Меня зовут {{ name }}.’)
>>>c = template.Context({‘name’: ‘Адриан’})
>>>print t.render(c)
Меня зовут Адриан.
>>>c = template.Context({‘name’: ‘Фред’})
>>>print t.render(c)
Меня зовут Фред.
В следующих разделах эти шаги описываются гораздо более подробно.
Создание объектов Template
Проще всего создать объект Template непосредственно. Класс Template находится в модуле django.template, его конструктор принимает единственный аргумент – код шаблона. Запустим интерактивный интерпретатор Python и посмотрим, как это работает в конкретной программе.
Находясь в каталоге проекта mysite, созданного командой django-admin. py startproject (см. главу 2), выполните команду python manage.py shell, чтобы войти в интерактивный интерпретатор.
Специальное приглашение Python
Если вы работали с Python раньше, то может возникнуть вопрос, почему нужно запускать оболочку командой python manage.py shell, а не просто python. Та и другая запускают интерактивный интерпретатор, но у команды manage.py shell есть одно важное отличие: до запуска интерпретатора она сообщает Django о том, какой файл параметров следует использовать. От этих параметров зависят многие части фреймворка Django, в том числе система шаб лонов, и, чтобы ими можно было воспользоваться, фреймворк должен знать, откуда взять параметры.
Для тех, кому интересно, расскажем, что происходит за кулисами. Django ищет переменную окружения DJANGO_SETTINGS_MODULE, значением которой должен быть путь импорта для файла settings. py. Например, DJANGO_SETTINGS_MODULE может быть равна ‘mysite. settings’ в предположении, что mysite включен в путь Python.
Если оболочка запускается командой python manage.py shell, то она берет на себя все хлопоты по установке значения переменной DJANGO_SETTINGS_MODULE. Мы рекомендуем во всех примерах использовать команду manage.py shell, чтобы избавить себя от необходимости самостоятельно задавать конфигурацию.
64 |
Глава 4. Шаблоны |
Когда вы получше освоитесь с Django, то, наверное, перестанете использовать команду manage.py 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 в следующих случаях:
•• Недопустимый тег
•• Недопустимые аргументы допустимого тега
•• Недопустимый фильтр
•• Недопустимые аргументы допустимого фильтра
•• Недопустимый синтаксис шаблона
•• Незакрытые теги (если требуется наличие закрывающего тега)
Использование системы шаблонов |
65 |
Отображение шаблона
Объекту Template можно передать данные в виде контекста. Контекст – это просто набор именованных переменных шаблона вместе с их значениями. Шаблон использует контекст, чтобы инициализировать свои переменные и вычислить теги.
В Django контекст представляется классом Context, который находится
вмодуле django.template. Его конструктор принимает один необязательный параметр – словарь, отображающий имена переменных в их значения. Вызовем метод render() объекта Template, передав ему контекст для
«заполнения» шаблона:
>>>from django.template import Context, Template
>>>t = Template(‘Меня зовут {{ name }}.’)
>>>c = Context({‘name’: ‘Стефан’})
>>>t.render(c)
u’Меня зовут Стефан.’
Подчеркнем, что метод t.render(c) возвращает объект Unicode, а не обычную строку Python. Это видно по наличию буквы u перед строкой. Объекты Unicode используются вместо строк повсюду в фреймворке Django. Если вы понимаете, к каким последствиям это приводит, то благодарите Django за все те ухищрения, на которые он идет, чтобы все работало правильно. А если не понимаете, то и не забивайте себе голову; просто помните, что поддержка Unicode в Django позволяет приложениям относительно безболезненно работать с разнообразными наборами символов, помимо обычной латиницы.
Словари и контексты
ВязыкеPythonсловаремназываетсяотображениемеждуключами
изначениями. Объект Context похож на словарь, но обладает дополнительной функциональностью, которая рассматривается в главе 9.
Имя переменной должно начинаться с буквы (A–Z или a–z) и может содержать буквы, цифры, знаки подчеркивания и точки. (Точки – это особый случай, который мы обсудим в разделе «Поиск контекстных переменных».) Строчные и заглавные буквы в именах переменных считаются различными.
Ниже показан пример компиляции и отображения шаблона, очень похожего на тот, что приведен в начале главы.
>>>from django.template import Template, Context
>>>raw_template = “””<p>Уважаемый(ая) {{ person_name }},</p>
...
66 Глава 4. Шаблоны
... <p>Спасибо, что вы сделали заказ в {{ company }}. Он будет
... доставлен вам {{ ship_date|date:”F j, Y” }}.</p>
...
... {% if ordered_warranty %}
... <p> Сведения о гарантийных обязательствах вы найдете внутри упаковки.</p>
... {% else %}
... <p> Вы не заказывали гарантию, поэтому должны будете действовать самостоятельно, когда изделие неизбежно выйдет из строя.</p>
... {% endif %}
...
... <p>С уважением,<br />{{ company }}</p>”””
>>>t = Template(raw_template)
>>>import datetime
>>>c = 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. Он будет\n доставлен вам April 2, 2009.</p>\n\n\n<p> Вы не заказывали гарантию, поэтому должны будете действовать \n самостоятельно, когда изделие неизбежно выйдет из строя.</p>\n\n\n<p>
С уважением,<br />Outdoor Equipment </p>”
Рассмотрим этот код по частям:
1.Сначала из модуля django.template импортируются классы Template
и Context.
2.Исходный текст шаблона мы сохраняем в переменной raw_template. Обратите внимание, что строка начинается и завершается тремя знаками кавычек, так как продолжается в нескольких строчках; строки, ограниченные с двух сторон одним знаком кавычки, не могут занимать несколько строчек.
3.Далее мы создаем объект шаблона t, передавая raw_template конструктору класса Template.
4.Импортируем модуль datetime из стандартной библиотеки Python, поскольку он понадобится в следующей инструкции.
5.Создаем объект с класса Context. Конструктор класса Context принимает словарь Python, который отображает имена переменных в их значения. Так, мы указываем, что переменная person_name содержит значение ‘Джон Смит’, переменная company – значение ‘Outdoor Equipment’ и так далее.
6.Наконец, мы вызываем метод render(), передавая ему контекст. Он возвращает результат отображения шаблона, в котором шаблонные переменные заменены фактическими значениями и обработаны все шаблонные теги.
Использование системы шаблонов |
67 |
Отметим, что выведен абзац «Вы не заказывали гарантию», поскольку переменная ordered_warranty равна False. Также отметим, что дата April 2, 2009 отформатирована в соответствии с форматом ‘F j, Y’. (Спецификаторы формата для фильтра date описываются в приложении E.)
У человека, не знакомого с языком Python, может возникнуть вопрос, почему, вместо того, чтобы просто разрывать строки, мы выводим символы перевода строки (‘\n’). Это объясняется одной тонкостью в интерактивном интерпретаторе 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, которые из-за больших накладных расходов на
68 |
Глава 4. Шаблоны |
работу анализатора XML оказываются на несколько порядков медленнее механизма отображения шаблонов в Django.
Поиск контекстных переменных
В приведенных выше примерах мы передавали в контексте простые переменные – в основном строки да еще дату. Однако система шаблонов способна элегантно обрабатывать и более сложные структуры данных – списки, словари и пользовательские объекты.
Ключом к обработке сложных структур данных в шаблонах Django является знак точки (.). Точка позволяет получить доступ к словарю по ключу, к элементам списка по индексу, а также к атрибутам и методам объекта.
Лучше всего проиллюстрировать ее использование на примерах. Пусть, например, мы передаем в шаблон словарь Python. Чтобы получить доступ к хранящимся в словаре значениям по ключу, воспользуемся точкой:
>>>from django.template import Template, Context
>>>person = {‘name’: ‘Sally’, ‘age’: ‘43’}
>>>t = Template(‘{{ person.name }} is {{ person.age }} years old.’)
>>>c = 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 = Template(‘Месяц равен {{ date.month }}, а год равен {{ date.year }}.’)
>>>c = 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):
Использование системы шаблонов |
69 |
|
... |
self.first_name, self.last_name = first_name, last_name |
|
>>>t = Template(‘Привет, {{ person.first_name }} {{ person.last_name }}.’)
>>>c = 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 = Template(‘Элемент 2 – {{ items.2 }}.’)
>>>c = Context({‘items’: [‘яблоки’, ‘бананы’, ‘морковки’]})
>>>t.render(c)
u’Элемент 2 - морковки.’
Отрицательные индексы не допускаются. Например, обращение к шаб лонной переменной {{ items.-1 }} приведет к исключению TemplateSyntax Error.
Списки в языке Python
Напомним, что в языке Python нумерация элементов списка начинается с 0. Индекс первого элемента равен 0, второго – 1 и т. д.
Когда система встречает в шаблоне точку в имени переменной, она производит поиск подходящей переменной в следующем порядке:
•• Доступ к словарю (например, foo[“bar”])
•• Доступ к атрибуту (например, foo.bar)
•• Вызов метода (например, foo.bar())
•• Доступ к списку по индексу (например, foo[2])
Поиск останавливается, как только будет найдено первое подходящее имя.