Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Django_-_podrobnoe_rukovodstvo

.pdf
Скачиваний:
306
Добавлен:
01.03.2016
Размер:
4.88 Mб
Скачать

80

Глава 4. Шаблоны­

С учетом вышесказанного вам, наверное, будет интересно узнать, что Django не заставляет вас пользоваться его собственным языком шаб­ лонов. Поскольку Django задуман как полноценный фреймворк, предоставляющий все необходимое для продуктивного труда вебразработчиков, чаще всего удобнее применять встроенную в него сис­ тему шаблонов,­ а не какую-нибудь другую библиотеку на Python, но это не является непререкаемым требованием. Ниже, в разделе «Использование шаблонов­ в представлениях», вы увидите, насколько просто использовать в Django любой другой язык шаблонов­.

Тем не менее мы без сомнения отдаем предпочтение системе­ шаблонов, встроенной в Django. Своими корнями она уходит в разработку веб-при­ ложений для сайта World Online и основана на богатом совокупном опыте создателей Django. Вот некоторые из положенных в ее основу идеологических принципов:

•• Бизнес-логика должна быть отделена от логики представления. Разработчики Django рассматривают систему­ шаблонов­ как инструмент, который управляет представлением и связанной с ним логикой, – и только. Система шаблонов­ не должна поддерживать функциональность, выходящую за эти пределы.

По этой причине невозможно вызывать написанный на Python код непосредственно из шаблонов­ Django. «Программирование» принципиально ограничено теми действиями, которые заложены в шаб­ лонные теги. Есть возможность написать пользовательские шаб­ лонные теги, умеющие выполнять произвольные действия, но готовые теги Django не позволяют выполнять произвольный код на Python, и это сознательное решение.

•• Синтаксис не должен быть привязан к HTML/XML. Хотя система­ шаблонов­ Django применяется главным образом для генерации HTML-разметки, она ничуть не менее пригодна и для других форматов, например, обычного текста. Существуют языки шаблонов,­ основанные на XML, в которых вся логика определяется тегами или атрибутами XML, но разработчики Django сознательно отказались от такого решения. Требование записывать шаблоны­ в виде корректного XML-документа открывает обширное пространство ошибок при записи и невнятных сообщений о них. Кроме того, накладные расходы, связанные с синтаксическим анализом XML-документа при обработке шаблонов,­ неприемлемо высоки.

•• Предполагается, что дизайнеры уверенно владеют HTML. При проектировании системы­ шаблонов­ не ставилась задача любой ценой добиться красивого отображения в таких WYSIWYG-редакторах, как Dreamweaver. Это слишком жесткое ограничение, которое не дало бы возможности сделать синтаксис таким удобным, как сейчас. Разработчики Django полагают, что авторы шаблонов­ вполне способны редактировать HTML-код вручную.

Использование шаблонов­ в представлениях

81

•• Не предполагается, что дизайнеры умеют программировать на Python. Авторы системы­ шаблонов­ понимали, что шаблоны­ вебстраниц чаще всего пишут дизайнеры, а не программисты, поэтому и не предполагали знакомство с языком Python.

Однако система­ адаптируется также под нужды небольших коллективов, в которых шаблоны­ создают именно программисты. Она позволяет расширять синтаксис за счет написания кода на Python. (Подробнее об этом см. главу 9.)

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

Использование шаблонов­ в представлениях

Познакомившись с основами использования системы­ шаблонов,­ мы теперь применим свои знания к созданию представления. Вспомните представление current_datetime в модуле mysite.views, которое мы написали в предыдущей главе. Вот оно:

from django.http import HttpResponse import datetime

def current_datetime(request): now = datetime.datetime.now()

html = “<html><body>Сейчас %s.</body></html>” % now return HttpResponse(html)

Изменим это представление, воспользовавшись системой­ шаблонов­ Django. Первая попытка могла бы выглядеть примерно так:

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

import datetime

def current_datetime(request): now = datetime.datetime.now()

t = Template(“<html><body>Сейчас {{ current_date }}.</body></html>”) html = t.render(Context({‘current_date’: now}))

return HttpResponse(html)

Да, здесь задействована система­ шаблонов,­ но проблемы, о которых мы говорили в начале этой главы, так и не решены. Точнее, шаблон­ все еще встроен в код на Python, так что настоящего отделения данных от внешнего вида мы не добились. Исправим этот недостаток, поместив шаблон­ в отдельный файл, который будет загружать функция представления.

Первое, что приходит в голову, – сохранить шаблон­ где-нибудь в файловой системе­ и воспользоваться имеющимися в Python средствами работы с файлами, чтобы прочитать его содержимое. Если предположить,

82

Глава 4. Шаблоны­

что шаблон­ сохранен в файле /home/djangouser/templates/mytemplate.html, такое решение можно было бы записать следующим образом:

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

import datetime

def current_datetime(request): now = datetime.datetime.now()

#Простой способ считать шаблон­ из файловой системы­.

#ПЛОХО, потому что не обрабатывается случай, когда файл

#отсутствует!

fp = open(‘/home/djangouser/templates/mytemplate.html’) t = Template(fp.read())

fp.close()

html = t.render(Context({‘current_date’: now})) return HttpResponse(html)

Но такой подход трудно назвать элегантным по нескольким причинам:

•• Не обрабатывается случай отсутствия файла, как отмечено в комментарии. Если файл mytemplate.html не существует или недоступен для чтения, то вызов open() возбудит исключение IOError.

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

•• Содержит много повторяющегося шаблонного­ кода. Вы могли бы заняться решением более интересных задач вместо того, чтобы писать обращения к функциям open(), fp.read() и fp.close().

Для решения этих проблем мы воспользуемся механизмами загрузки шабл­онов и наследования шаблонов­.

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

В Django предлагается удобный и мощный API для загрузки шаблонов­ из файловой системы­. Он ставит целью устранить дублирование кода при загрузке шаблонов­ и в самих шаблонах­.

Чтобы воспользоваться API загрузки шаблонов,­ нужно первым делом сообщить фреймворку, где хранятся шаблоны­. Это задается в файле параметров settings.py, о котором мы упоминали в предыдущей главе в связи с параметром ROOT_URLCONF.

Если вы выполняли все упражнения, то сейчас откройте файл settings. py и найдите в нем параметр TEMPLATE_DIRS. По умолчанию он содержит пустой кортеж, в котором есть только автоматически сгенерированные комментарии:

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

83

TEMPLATE_DIRS = (

#Поместите сюда строки, например, “/home/html/django_templates”

#или “C:/www/django/templates”.

#Всегда используйте только символы прямого слеша, даже в Windows.

#Не забывайте, что пути к файлам должны быть абсолютными,

#а не относительными.

)

Этот параметр сообщает механизму загрузки шаблонов­ Django, где искать шаблоны­. Выберите каталог, в котором будут храниться ваши шаблоны,­ и добавьте его в TEMPLATE_DIRS:

TEMPLATE_DIRS = ( ‘/home/django/mysite/templates’,

)

Необходимо отметить следующее.

•• Можно указывать любой каталог, лишь бы учетная запись, от имени которой работает веб-сервер, имела права чтения для него и находящихся в нем файлов. Если вы не можете решить, где хранить шаб­ лоны, то мы рекомендуем создать подкаталог templates в каталоге проекта (то есть в каталоге mysite, созданном в главе 2).

•• Если кортеж TEMPLATE_DIRS содержит всего один каталог, не забудьте поставить запятую после строки, задающей путь к нему!

Так писать нельзя:

#Отсутствует запятая! TEMPLATE_DIRS = (

‘/home/django/mysite/templates’

)

Атак можно:

#Запятая на месте.

TEMPLATE_DIRS = ( ‘/home/django/mysite/templates’,

)

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

•• На платформе Windows следует указывать букву диска и разделять компоненты пути знаками прямого, а не обратного слеша:

TEMPLATE_DIRS = ( ‘C:/www/django/templates’,

)

•• Проще всего использовать абсолютные пути (начинающиеся от корня файловой системы)­ . Но если вы предпочитаете чуть более гиб-

84

Глава 4. Шаблоны­

кое и автоматизированное решение, то можете воспользоваться тем фактом, что файл параметров в Django – это просто код на Python, и строить содержимое кортежа TEMPLATE_DIRS динамически, например:

import os.path

TEMPLATE_DIRS = (

os.path.join(os.path.dirname(__file__), ‘templates’). replace(‘\\’,’/’),

)

В этом примере используется «магическая» переменная Python __file__, в которую автоматически записывается имя файла, содержащего Python-модуль. В данном случае мы получаем имя каталога, в котором находится файл settings.py (os.path.dirname), и объединяем его с именем templates платформенно-независимым способом (os.path. join), а затем всюду заменяем обратный слеш прямым (это необходимо в случае Windows).

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

Установив TEMPLATE_DIRS, изменим код представления, воспользовав­ шись механизмом загрузки шаблонов­ вместо «зашивания» путей в код. Модифицируем представление current_datetime следующим образом:

from django.template.loader import get_template from django.template import Context

from django.http import HttpResponse import datetime

def current_datetime(request): now = datetime.datetime.now()

t = get_template(‘current_datetime.html’)

html = t.render(Context({‘current_date’: now})) return HttpResponse(html)

Вэтом примере вместо того, чтобы загружать шаблон­ из файловой системы­ вручную, мы вызываем функцию django.template.loader.get_ template(). Эта функция принимает имя шаблона,­ определяет, где находится соответствующий ему файл, открывает этот файл и возвращает откомпилированный шаблон­ в виде объекта Template.

Вэтом примере мы назвали шаблон­ current_datetime.html, но ничего специального в расширении .html нет. Можно было бы взять любое разумное с вашей точки зрения расширение или вообще обойтись без расширения.

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

85

Чтобы найти шаблон­ в файловой системе,­ функция get_template() поочередно соединяет каталоги, перечисленные в параметре TEMPLATE_DIRS, с именем шаблона­. Например, если TEMPLATE_DIRS содержит путь ‘/home/ django/mysite/templates’, то get_template() будет искать шаблон­ в файле / home/django/mysite/templates/current_datetime.html.

Если функция get_template() не найдет шаблон­ с указанным именем, она возбудит исключение TemplateDoesNotExist. Чтобы посмотреть, как это выглядит, запустите сервер разработки Django командой python manage.py runserver, находясь в каталоге проекта. Затем в броузере введите адрес страницы, активирующей представление current_datetime (например, http://127.0.0.1:8000/time/). В предположении, что параметр DEBUG равен True и шаблон­ current_datetime.html еще не создан, вы увидите страницу ошибок Django, на которой большими буквами сообщается об ошибке TemplateDoesNotExist (рис. 4.1).

Рис. 4.1. Страница ошибок, отображаемая, когда шаблон­ не найден

Эта страница ошибок похожа на обсуждавшуюся в главе 3, но присутствует дополнительная отладочная информация: раздел Template-loader postmortem (Дамп аварийного завершения загрузчика шаблонов),­ в котором говорится, какие шаблоны­ Django пытался загрузить и почему попытка не удалась (например, «File does not exist» (Файл не существует)). Эта информация поистине бесценна для отладки ошибок загрузки шаблонов­.

Теперь создайте файл current_datetime.html в своем каталоге шаблонов­ и поместите в него такой код:

<html><body>Сейчас {{ current_date }}.</body></html>

Обновив страницу в броузере, вы увидите ожидаемый результат.

86

Глава 4. Шаблоны­

render_to_response()

Мы показали, как загрузить шаблон,­ заполнить контекст (Context) и вернуть объект HttpResponse, содержащий результат отображения шаблона­. Мы оптимизировали эту процедуру, воспользовавшись функцией get_template() вместо жесткого определения имен и путей в коде. Но все равно приходится много печатать. Поскольку описанная последовательность шагов – общеупотребительная идиома, Django предлагает вспомогательную функцию, позволяющую загрузить шаблон,­ отобразить его и получить объект HttpResponse в одной строчке кода.

Речь идет о функции render_to_response(), находящейся в модуле django.shortcuts. Как правило, вы будете пользоваться этой функцией, а не заниматься загрузкой шаблонов­ и созданием объектов Context и HttpResponse вручную, если только работодатель не оценивает ваш труд по количеству написанных строчек кода.

Вот как выглядит представление current_datetime, переписанное с использованием функции render_to_response():

from django.shortcuts import render_to_response import datetime

def current_datetime(request): now = datetime.datetime.now()

return render_to_response(‘current_datetime.html’, {‘current_date’: now})

Согласитесь, разница впечатляет! Рассмотрим изменения более подробно.

•• Нам больше не нужно импортировать get_template, Template, Context, HttpResponse. Вместо них импортируется только функция django. shortcuts.render_to_response. Но инструкция import datetime осталась.

•• В функции current_datetime мы по-прежнему вычисляем значение переменной now, но заботу о загрузке шаблона,­ создании контекста, отображении шаблона­ и создании объекта HttpResponse взяла на себя render_to_response(). Поскольку эта функция возвращает объект HttpResponse, мы можем просто вернуть полученное от нее значение как результат работы функции представления.

•• В первом аргументе функции render_to_response() передается имя шаблона­. Второй аргумент, если он задан, должен содержать словарь, необходимый для создания контекста этого шаблона­. Если второй аргумент отсутствует, то render_to_response() будет использовать пустой словарь.

Трюк с функцией locals()

Рассмотрим последний вариант представления current_datetime:

def current_datetime(request): now = datetime.datetime.now()

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

87

return render_to_response(‘current_datetime.html’, {‘current_date’: now})

Очень часто нам приходится вычислять какие-то значения, сохранять их в переменных (переменная now в данном примере) и передавать переменные в шаблон­. Особо ленивые программисты отметят, что давать имена и временным, и шаблонным­ переменным, пожалуй, лишнее. Да и печатать приходится больше.

Если вы принадлежите к числу таких лентяев и любите сокращать код до предела, то можете воспользоваться встроенной в Python функцией locals(). Она возвращает словарь, отображающий имена всех локальных переменных на их значения, где под локальными понимаются переменные, определенные в текущей области видимости. С ее помощью наше представление можно переписать в таком виде:

def current_datetime(request): current_date = datetime.datetime.now()

return render_to_response(‘current_datetime.html’, locals())

Здесь, вместо того чтобы вручную задавать содержимое контекста, мы передаем словарь, возвращаемый функцией locals(), который содержит все переменные, определенные в данной точке выполнения. Поэтому мы переименовали переменную now в current_date, поскольку именно так она называется в шаблоне­. В данном примере locals() не дает значительного выигрыша, но эта техника может избавить вас от лишнего стучания по клавиатуре в случае, когда шаблонных­ переменных много или вам просто лень печатать.

Однако при использовании locals() следует помнить, что возвращаемый ею словарь включает все локальные переменные, а их может быть гораздо больше, чем реально необходимо шаблону­. Так, в рассматриваемом примере этот словарь содержит также переменную request. Существенно это или нет, зависит от конкретного приложения и вашего стремления к совершенству.

Подкаталоги в get_template()

Хранить все шаблоны­ в одном каталоге может оказаться неудобно. Иногда хочется организовать несколько подкаталогов в каталоге шаблона,­ и в таком желании нет ничего плохого. Мы даже рекомендуем такой подход; некоторые продвинутые средства Django (например, система­ обобщенных представлений, о которой мы расскажем в главе 11) по умолчанию ожидают, что шаблоны­ организованы именно таким образом.

Распределить шаблоны­ по нескольким подкаталогам несложно. Достаточно при вызове функции get_template() указать имя подкаталога и символ слеша перед именем шаблона,­ например:

t = get_template(‘dateapp/current_datetime.html’)

88

Глава 4. Шаблоны­

Поскольку render_to_response() – всего лишь тонкая обертка вокруг get_ template(), то же самое можно проделать с первым аргументом render_to_ response():

return render_to_response(‘dateapp/current_datetime.html’, {‘current_date’: now})

Глубина дерева подкаталогов ничем не ограничена. Можете завести столько уровней, сколько потребуется.

Примечание

В Windows не забывайте использовать прямой слеш вместо обратного. Функция get_template() следует принятому в UNIX соглашению о задании путей.

Шаблонный­ тег include

Теперь, освоив механизм загрузки шаблонов,­ мы познакомимся со встроенным шаблонным­ тегом {% include %}, в котором этот механизм применяется. Этот тег позволяет включить содержимое другого шаб­ лона. В качестве аргумента ему передается имя включаемого шаблона,­ которое может быть переменной или строковым литералом в одиночных или двойных кавычках. Если вы обнаруживаете, что один и тот же код встречается в нескольких шаблонах,­ подумайте о том, чтобы вынести его в отдельный шаблон,­ включаемый с помощью {% include %}.

В следующих двух примерах включается содержимое шаблона­ nav.html. Они эквивалентны и иллюстрируют тот факт, что допустимы как одиночные, так и двойные кавычки:

{% include ‘nav.html’ %} {% include “nav.html” %}

В следующем примере включается содержимое шаблона­ includes/nav. html:

{% include ‘includes/nav.html’ %}

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

{% include template_name %}

Как и в функции get_template(), полное имя файла шаблона­ определяется как результат конкатенации пути к каталогу из TEMPLATE_DIRS с именем запрошенного шаблона­.

Отображение включаемого шаблона­ выполняется в контексте включающего. Рассмотрим, к примеру, следующие два шаблона:­

# mypage.html

<html>

<body>

{% include “includes/nav.html” %}

Наследование шаблонов­

89

<h1>{{ title }}</h1> </body>

</html>

# includes/nav.html

<div id=”nav”>

Вы находитесь в: {{ current_section }} </div>

Если отображение mypage.html выполняется в контексте, содержащем переменную current_section, то она будет доступна и во включаемом шаб­ лоне.

Если шаблон,­ указанный в теге {% include %}, не будет найден, то Django отреагирует следующим образом:

•• Если параметр DEBUG равен True, то появится страница ошибок с сообщением об исключении TemplateDoesNotExist;

•• Если параметр DEBUG равен False, то тег будет просто проигнорирован, то есть на его месте ничего не отобразится.

Наследование шаблонов­

Рассмотренные до сих пор примеры шаблонов­ представляли собой крохотные фрагменты HTML, но в настоящих приложениях с помощью системы­ шаблонов­ Django создаются полномасштабные HTML-стра­ ницы­. В результате встает типичный для веб-разработки вопрос: как устранить дублирование общих областей, например, встречающейся на всех страницах сайта области навигации?

Классически эта проблема решалась с помощью включения на стороне сервера, то есть размещения на HTML-странице директив, требующих включения других страниц. Как было описано выше, Django под­ держивает такой подход с помощью шаблонного­ тега {% include %}. Но предпочтительным является более элегантное решение, называемое наследованием шаблонов­.

Смысл механизма наследования шаблонов­ в том, чтобы создать базовый «шаблон­-скелет», который содержит общие части сайта и определяет «блоки», переопределяемые в дочерних шаблонах­.

Рассмотрим, как это делается, на примере более полного шаблона­ для нашего представления current_datetime, для чего отредактируем файл current_datetime.html:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”ru”>

<head>

<title>Текущее время</title> </head>

<body>

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]