Объект RequestContext и контекстные процессоры
Для отображения шаблона необходим контекст. Обычно таковым является экземпляр класса django.template.Context, но в Django имеется также специальный подкласс django.template.RequestContext, который действует несколько иначе. RequestContext автоматически помещает в контекст несколько дополнительных переменных, например, объект HttpRequest или информацию о текущем аутентифицированном пользователе.
Объект RequestContext можно использовать в тех случаях, когда необходимо передать один и тот же набор переменных в несколько шаблонов. Рассмотрим, к примеру, следующие два представления:
from django.template import loader, Context
def view_1(request): tt . ..
t = loader.get_template(‘template"!.html’) с = Context({
‘app’ : ‘My app’,
‘user’: request.user,
‘ip_address’: request.META[‘REMOTE_ADDR’], ‘message’: ‘ Я - представление 1.’
})
return t.render(c)
def view_2(request): tt …
t = loader.get_template(‘template2.html’) с = Context({
‘app’: ‘My app’, ‘user’: request.user,
‘ip_address’: request.META[‘REMOTE_ADDR’], ‘message’: ‘Я - второе представление.’
})
return t.render(c)
(Обратите внимание, что в этих примерах мы сознательно не используем вспомогательную функцию render_to_response(), а вручную загружаем шаблоны, конструируем контекстные объекты и выполняем отображение. Мы «выписываем» все шаги, чтобы было понятно, что присходит.)
Оба представления передают в шаблон одни и те же переменные: app, user и ip_address. Было бы неплохо избавиться от подобного дублирования.
Объект RequestContext и контекстные процессоры специально придуманы для решения этой задачи. Контекстный процессор позволяет определить ряд переменных, автоматически добавляемых в каждый контекст, избавляя вас от необходимости задавать их при каждом обращении к render_to_response(). Надо лишь при отображении шаблона использовать объект RequestContext вместо Context.
Самый низкоуровневый способ применения контекстных процессоров заключается в том, чтобы создать несколько процессоров и передать их объекту RequestContext. Вот как можно было бы переписать предыдущий пример с использованием контекстных процессоров:
from django.template import loader, RequestContext
def custom_proc(request):
"Контекстный процессор, добавляющий ‘app’, ‘user’ и ‘ip_address’." return {
‘app’: ‘My app’, ‘user’: request.user,
‘ip_address’: request.META[‘REMOTE_ADDR’]
}
def view_1(request):
t = loader.get_template(‘templatel.html’)
с = RequestContext(request, {‘message’: ‘Я - представление 1.’}, processors=[custom_proc])
return t.render(c)
def view_2(request): tt . ..
t = loader.get_template(‘template2.html’ )
с = RequestContext(request, {‘message’: ‘Я - второе представление.’},
processors=[custom_proc]) return t.render(c)
Рассмотрим этот код более внимательно.
• Сначала мы определяем функцию custom_proc. Это контекстный процессор - он принимает объект HttpRequest и возвращает словарь переменных, который будет использован в контексте шаблона. Больше он ничего не делает.
• Мы изменили обе функции представления так, что теперь вместо Context в них используется RequestContext. Такой способ конструирования контекста имеет два отличия. Во-первых, конструктор Request- Context требует, чтобы первым аргументом был объект HttpRequest - тот самый, который с самого начала был передан функции представления. Во-вторых, конструктор RequestContext принимает необязательный аргумент processors - список или кортеж функций - контекстных процессоров. В данном случае мы передаем одну функцию custom_proc, которую определили выше.
• Теперь в контексты, конструируемые в представлениях, не нужно включать переменные арр, user, ip_address, потому что они предоставляются функцией custom_proc.
• Однако каждое представление сохраняет возможность включить в контекст любые дополнительные шаблонные переменные, которые ему могут понадобиться. В данном случае переменная message получает разные значения.
В главе 4 мы познакомились со вспомогательной функцией render_to_ responseQ, которая избавляет от необходимости вызывать loader.get_ templateO, создавать объект Context и обращаться к методу шаблона renderQ. Чтобы продемонстрировать низкоуровневый механизм работы контекстных процессоров, мы в предыдущих примерах обошлись без render_to_response(). Однако возможно и даже рекомендуется использовать контекстные процессоры в сочетании с render_to_response(). Для этого предназначен аргумент context_instance:
from django.shortcuts import render_to_response from django.template import RequestContext
def custom_proc(request):
"Контекстный процессор, добавляющий ‘арр’, ‘user’ and ‘ip_address’." return {
‘app’: ‘My app’, ‘user’: request.user, ‘ip_address’: request.META[‘REMOTE_ADDR’]
}
def view_1(request): «…
return render_to_response(‘templatel.html’, {‘message’: ‘Я - представление 1.’},
context_instance=RequestContext(request, processors=[custom_proc]))
def view_2(request): tt . ..
return render_to_response(‘template2.html’, {‘message’: ‘Я - второе представление.’},
context.instance^RequestContextCrequest, processors=[custom_proc]))
Здесь мы свели код отображения шаблона в каждом представлении к одной строчке.
Это улучшение, но, оценивая лаконичность кода, мы должны признать, что теперь опустилась другая чаша весов. Мы избавились от дублирования данных (шаблонных переменных), зато появилось дублирование кода (при передаче аргумента processors). Поскольку теперь всякий раз приходится набирать processors, то получается, что использование контекстных процессоров мало что сэкономило.
Поэтому Django поддерживает глобальные контекстные процессоры. Параметр TEMPLATE_CONTEXT_PROCESSORS (в файле settings.ру) определяет контекстные процессоры, которые всегда должны применяться к Request- Context. Это избавляет от необходимости передавать аргумент processors при каждом использовании RequestContext.
По умолчанию TEMPLATE_CONTEXT_PROCESSORS определен следующим образом:
TEMPLATE_CONTEXT_PROCESSORS = (
‘django.core.context_processors.auth’, ‘django.core.context_processors.debug’, ‘django.core.context_processors.i18n’, ‘django.core.context_processors.media’,
)
Этот параметр представляет собой кортеж вызываемых объектов с таким же интерфейсом, как у рассмотренной выше функции custom_proc: они принимают в качестве аргумента объект запроса и возвращают словарь элементов, добавляемых в контекст. Отметим, что значения в кортеже TEMPLATE_CONTEXT_PROCESSORS задаются в виде строк, то есть процессоры должны находиться в пути Python (чтобы на них можно было сослаться из файла параметров).
Процессоры применяются в указанном порядке. Если один процессор добавил в контекст некоторую переменную, а затем другой процессор
добавил переменную с таким же именем, то первое определение будет затерто.
Django предоставляет несколько простых контекстных процессоров, включая применяемые умолчанию.
django.core.context_processors.auth
Если TEMPLATE_CONTEXT_PROCESSORS включает этот процессор, то все объекты RequestContext будут содержать следующие переменные:
• user: экземпляр класса django.contrib.auth.models.User, описывающий текущего аутентифицированного пользователя (или экземпляр класса Anonymousllser, если пользователь не аутентифицирован).
• messages: список сообщений (в виде строк) для текущего аутентифицированного пользователя. В действительности при обращении к этой переменной каждый раз происходит вызов метода request. user. get_and_delete_messages(), который извлекает сообщения данного пользователя и удаляет их из базы данных.
• perms: экземпляр класса django.core.context_processors.PermWrapper, представляющий разрешения текущего аутентифицированного пользователя.
Дополнительные сведения о пользователях, разрешениях и сообщениях см. в главе 14.
django.core.context_processors.debug
Этот процессор передает отладочную информацию на уровень шаблона. Если TEMPLATE_CONTEXT_PROCESSORS включает этот процессор, то все объекты RequestContext будут содержать следующие переменные:
• debug: значение параметра DEBUG (True или False). Обратившись к этой переменной, шаблон может узнать, работает ли приложение в режиме отладки.
• sql_queries: список словарей {‘sql*: ‘time’: …}, в котором представлены все SQL-запросы, произведенные в ходе обработки данного запроса, и время выполнения каждого из них. Запросы следуют в порядке выполнения.
Поскольку отладочная информация конфиденциальна, этот процессор добавляет переменные в контекст только при выполнении следующих условий:
• Параметр DEBUG равен True
• Запрос поступил с одного из IP-адресов, перечисленных в параметре
INTERNAL_IPS
Проницательный читатель заметит, что шаблонная переменная debug никогда не принимает значение False, так как если параметр DEBUG имеет значение False, то эта переменная вообще не добавляется в шаблон.
django.core.context_processors.i18n
Если этот процессор включен, то все объекты RequestContext будут содержать следующие переменные:
• LANGUAGES: значение параметра LANGUAGES.
• LANGUAGE_CODE: значение атрибута request.LANGUAGE_CODE, если он существует, в противном случае - значение параметра LANGUAGE_CODE.
Дополнительные сведения об этих параметрах см. в приложении D.
django.core.context_processors. request
Если этот процессор включен, то все объекты RequestContext будут содержать переменную request, которая является ссылкой на текущий объект HttpRequest. Отметим, что по умолчанию этот процессор не включен, его нужно активировать явно.
Этот процессор может потребоваться, если шаблонам необходим доступ к атрибутам текущего объекта HttpRequest, например, к IP-адресу клиента:
{{ request.REMOTE_ADDR }}
Как написать собственный контекстный процессор
Вот несколько советов:
• Ограничивайте функциональность одного контекстного процессора. Использовать несколько процессоров совсем несложно, поэтому лучше разбить функциональность на логически независимые части, что облегчит повторное использование в будущем.
• Не забывайте, что все контекстные процессоры, перечисленные в параметре TEMPLATE_CONTEXT_PROCESSORS, доступны любому шаблону, пользующемуся файлом параметров, поэтому старайтесь выбирать имена переменных так, чтобы не возникало конфликтов с переменными, уже встречающимися в шаблонах. Поскольку имена переменных чувствительны к регистру, будет разумно зарезервировать для переменных, добавляемых процессором, имена, написанные заглавными буквами.
• Безразлично, в каком каталоге файловой системы находятся процессоры, лишь бы он был включен в путь Python, чтобы на процессоры можно было сослаться из параметра TEMPLATE_CONTEXT_PROCESSORS. Тем не менее по соглашению принято помещать процессоры в файл context_processors.py в каталоге приложения или проекта.
Источник: Головатый А., Каплан-Мосс Дж. Django. Подробное руководство, 2-е издание. - Пер. с англ. - СПб.: Символ- Плюс, 2010. - 560 е., ил.