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

Django_-_podrobnoe_rukovodstvo

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

240

Глава 11. Обобщенные представления

имени приложения, в котором определена модель, а часть publisher – имени модели, записанному строчными буквами.

Отображение шаблона­ осуществляется в контексте, содержащем переменную object_list, в которой хранятся все объекты publisher. Вот пример очень простого шаблона:­

{% extends “base.html” %}

{% block content %} <h2>Издательства</h2> <ul>

{% for publisher in object_list %} <li>{{ publisher.name }}</li>

{% endfor %} </ul>

{% endblock %}

(Здесь предполагается, что существует шаблон­ с именем base.html, который мы создали в примере из главы 4.)

Вот и все. Богатство возможностей обобщенных представлений определяется тем, что передано в словаре «info». В приложении C описаны все имеющиеся обобщенные представления и их параметры. В оставшейся части главы мы рассмотрим некоторые типичные способы настройки и расширения обобщенных представлений.

Расширение обобщенных представлений

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

К счастью, почти всегда можно просто расширить имеющееся обобщенное представление. И ниже мы рассмотрим несколько типичных способов.

«Дружественный» контекст шаблона­

Вы, наверное, заметили, что в предыдущем примере шаблона­ списка вся информация об издательствах хранится в переменной object_list. Хотя такая реализация действует безупречно, она не слишком дружелюбна по отношению к автору шаблона:­ тот должен заранее знать, что имеет дело именно с издательствами. Удобнее было бы назвать переменную publisher_list, тогда ее содержимое не вызывало бы сомнений.

Изменить имя переменной легко можно с помощью аргумента template_ object_name:

from django.conf.urls.defaults import *

Расширение обобщенных представлений

241

from django.views.generic import list_detail from mysite.books.models import Publisher

publisher_info = {

‘queryset’: Publisher.objects.all(), ‘template_name’: ‘publisher_list_page.html’, ‘template_object_name’: ‘publisher’,

}

urlpatterns = patterns(‘’,

(r’^publishers/$’, list_detail.object_list, publisher_info)

)

Имя переменной, содержащей список, формируется путем добавления суффикса _list к значению template_object_name.

Задавать аргумент template_object_name всегда полезно; коллеги, занятые разработкой шаблонов,­ скажут вам спасибо.

Пополнение контекста

Иногда возникает потребность в дополнительной информации, которая

вобобщенном представлении отсутствует. Пусть, например, на странице с детальным описанием издательства необходимо вывести список всех остальных издательств. Обобщенное представление object_detail помещает в контекст сведения о данном издательстве, но передать

вшаблон­ список всех издательств, похоже, не получится.

Однако же способ есть: любое обобщенное представление принимает дополнительный параметр extra_context. Это словарь объектов, который будет добавлен в контекст шаблона­. То есть, чтобы передать список всех издательств в детальное представление, нужно построить словарь следующим образом:

publisher_info = {

‘queryset’: Publisher.objects.all(), ‘template_object_name’: ‘publisher’,

‘extra_context’: {‘publisher_list’: Publisher.objects.all()}

}

В результате мы получаем в контексте шаблона­ список значений для переменной {{ publisher_list }}. Этот прием можно использовать для передачи любой информации в шаблон­ обобщенного представления. Очень удобно. Однако здесь присутствует одна малозаметная ошибочка, сумеете найти ее сами?

Проблема возникает, когда при вычислении значений в extra_context выполняются запросы. Поскольку в этом примере Publisher.objects. all() входит в конфигурацию URL, то вызываться он будет только один раз (при первой загрузке конфигурации). Никакие операции добавления и удаления издательств не отразятся в обобщенном представлении

242

Глава 11. Обобщенные представления

до перезагрузки веб-сервера (о том, как вычисляются и кэшируются наборы QuerySet, рассказывается в разделе «Объекты QuerySet и кэширование» в приложении B).

Примечание

Эта проблема не возникает, когда объект QuerySet передается обобщенному представлению в аргументе. Поскольку Django знает, что этот объект никогда не должен кэшироваться, то обобщенное представление очищает кэш перед каждым отображением шаблона­.

Решение есть – вместо фактического значения в параметре extra_context следует передать функцию обратного вызова. Если значением extra_ context является вызываемый объект (то есть функция), то он будет вызываться на этапе отображения представления (а не один-единственный раз). Это можно сделать с помощью явно определенной функции:

def get_publishers():

return Publisher.objects.all()

publisher_info = {

‘queryset’: Publisher.objects.all(), ‘template_object_name’: ‘publisher’, ‘extra_context’: {‘publisher_list’: get_publishers}

}

Или менее очевидным, но более лаконичным способом – если вспомнить, что метод Publisher.objects.all сам по себе является вызываемым объектом:

publisher_info = {

‘queryset’: Publisher.objects.all(), ‘template_object_name’: ‘publisher’,

‘extra_context’: {‘publisher_list’: Publisher.objects.all}

}

Обратите внимание на отсутствие скобок после Publisher.objects.all. Это означает, что речь идет о ссылке на функцию, а не о ее вызове (она будет вызвана из обобщенного представления позже).

Представление подмножеств объектов

Теперь займемся ключом queryset, который мы использовали во всех примерах. Его принимают большинство обобщенных представлений – именно так представление узнает, какой набор объектов отображать (начальные сведения о классе QuerySet см. в разделе «Выбор объектов» в главе 5, а полное описание – в приложении B).

Допустим, что нам требуется отсортировать список книг в порядке убывания даты публикации:

Расширение обобщенных представлений

243

book_info = {

‘queryset’: Book.objects.order_by(‘-publication_date’),

}

urlpatterns = patterns(‘’,

(r’^publishers/$’, list_detail.object_list, publisher_info), (r’^books/$’, list_detail.object_list, book_info),

)

Пример несложный, но для иллюстрации идеи вполне подходит. Разумеется, обычно требуется нечто большее, чем простое упорядочение объектов. Этот же прием можно использовать для вывода списка книг, опубликованных конкретным издательством:

apress_books = {

‘queryset’: Book.objects.filter(publisher__name=’Apress Publishing’), ‘template_name’: ‘books/apress_list.html’

}

urlpatterns = patterns(‘’,

(r’^publishers/$’, list_detail.object_list, publisher_info), (r’^books/apress/$’, list_detail.object_list, apress_books),

)

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

Еще отметим, что это не самый элегантный способ вывести список книг одного издательства. Если бы мы решили добавить страницу еще одного издательства, то пришлось бы вносить изменения в конфигурацию URL, а при достаточно большом количестве издательств конфигурация стала бы слишком громоздкой. Эту проблему мы решим в следующем разделе.

Сложная фильтрация с помощью обертывающих функций

Часто возникает необходимость оставить на странице списка только объекты, определяемые некоторым ключом в URL. Выше мы «зашили» название издательства в конфигурацию URL, но что если потребуется написать представление, которое отображало бы книги, опубликованные произвольным издательством? Решение состоит в том, чтобы «обернуть» обобщенное представление object_list и тем самым избежать необходимости писать много кода вручную. Как обычно, начинаем с конфигурации URL:

urlpatterns = patterns(‘’,

(r’^publishers/$’, list_detail.object_list, publisher_info), (r’^books/(\w+)/$’, books_by_publisher),

)

244

Глава 11. Обобщенные представления

Теперь напишем само представление books_by_publisher:

from django.shortcuts import get_object_or_404 from django.views.generic import list_detail from mysite.books.models import Book, Publisher

def books_by_publisher(request, name):

#Найти издательство (если не найдено, возбудить ошибку 404). publisher = get_object_or_404(Publisher, name__iexact=name)

#Основная работа выполняется представлением object_list. return list_detail.object_list(

request,

queryset = Book.objects.filter(publisher=publisher), template_name = ‘books/books_by_publisher.html’, template_object_name = ‘book’,

extra_context = {‘publisher’: publisher}

)

Этот прием действует, потому что в обобщенных представлениях нет ничего особенного – это обычные функции на Python. Как и любая другая функция представления, обобщенное представление ожидает получить определенные аргументы и возвращает объект HttpResponse. Следовательно, совсем не сложно написать небольшую функцию, обертывающую обобщенное представление и выполняющую дополнительные действия до (или после, см. следующий раздел) его вызова.

Примечание

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

Реализация дополнительных действий

И напоследок рассмотрим, как можно реализовать дополнительные действия до или после вызова обобщенного представления.

Предположим, что в объекте Author имеется поле last_accessed, в котором хранится информация о моменте времени, когда кто-то в последний раз интересовался данным автором. Понятно, что обобщенное представление object_detail об этом поле ничего не знает, но нам не составит труда написать специальное представление для обновления этого поля.

Сначала добавим образец URL, который будет указывать на специальное представление author_detail:

from mysite.books.views import author_detail

urlpatterns = patterns(‘’,

# ...

Расширение обобщенных представлений

245

(r’^authors/(?P<author_id>\d+)/$’, author_detail),

# ...

)

Затем напишем обертывающую функцию:

import datetime

from django.shortcuts import get_object_or_404 from django.views.generic import list_detail from mysite.books.models import Author

def author_detail(request, author_id):

#Делегировать работу обобщенному представлению и получить от

#него HttpResponse.

response = list_detail.object_detail( request,

queryset = Author.objects.all(), object_id = author_id,

)

#Записать дату последнего доступа. Это делается *после*, а не

#до вызова object_detail(), чтобы этот код не вызывался

#для несуществующих объектов Author. (Если автора нет, то

#object_detail() возбудит исключение Http404 и мы сюда

#не попадем.)

now = datetime.datetime.now() Author.objects.filter(id=author_id).update(last_accessed=now)

return response

Примечание

Чтобы этот код заработал, необходимо добавить поле last_accessed в модель

Author и создать шаблон­ books/author_detail.html.

Аналогичную идиому можно использовать для изменения ответа, возвращаемого обобщенным представлением. Если бы нам потребовалось получить версию списка авторов в виде простого загружаемого текстового файла, можно было бы поступить следующим образом:

def author_list_plaintext(request): response = list_detail.object_list(

request,

queryset = Author.objects.all(), mimetype = ‘text/plain’,

template_name = ‘books/author_list.txt’

)

response[“Content-Disposition”] = “attachment; filename=authors.txt” return response

Это возможно благодаря тому, что обобщенное представление возвращает объект HttpResponse, то есть словарь, в который можно дописать HTTP-заголовки. Кстати, заголовок Content-Disposition говорит броузе-

246

Глава 11. Обобщенные представления

ру о необходимости загрузить файл и сохранить его, а не отображать в окне.

Что дальше?

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

На этом завершается раздел книги, посвященный профессиональному использованию. В следующей главе мы рассмотрим развертывание приложений Django.

12

Развертывание Django

В этой главе мы рассмотрим последний этап создания приложения Django: развертывание на действующем сервере.

Если вы следовали за нашими примерами, то, вероятно, уже пользовались сервером разработки (runserver), который очень упрощает жизнь (и избавляет от необходимости настраивать веб-сервер). Но этот сервер предназначен только для разработки на локальном компьютере, а не для публикации сайта в открытом Интернете. Для развертывания приложения Django понадобится мощный промышленный веб-сервер, например, Apache. В этой главе мы покажем, как это делается, но сначала приведем контрольный список того, что должно быть готово перед «выходом в свет».

Подготовка приложения к развертыванию на действующем сервере

К счастью, сервер разработки настолько хорошо аппроксимирует «настоящий» веб-сервер, что для подготовки приложения Django к работе на действующем сервере понадобится внести не так уж много изменений. Но ряд вещей сделать абсолютно необходимо.

Выключение режима отладки

Команда django-admin.py startproject, с помощью которой мы создали проект в главе 2, сгенерировала файл settings.py, в котором параметр DEBUG установлен в True. Различные компоненты Django проверяют этот параметр и в зависимости от его значения ведут себя так или иначе. Например, когда параметр DEBUG имеет значение True:

•• Все запросы к базе данных сохраняются в памяти в виде объекта django.db.connection.queries. Нетрудно понять, что памяти при этом расходуется немало!

248

Глава 12. Развертывание Django

•• Информация об ошибке 404 отображается на специальной странице ошибок (см. главу 3), хотя следовало бы вернуть ответ с кодом 404. Эта страница содержит конфиденциальные данные, которые не должны демонстрироваться любому посетителю в Интернете.

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

Короче говоря, когда DEBUG равно True, Django считает, что с сайтом работает программист, которому можно доверять. Интернет же полон хулиганов, не заслуживающих никакого доверия, и поэтому, готовясь к развертыванию приложения, первым делом установите параметр

DEBUG в False.

Выключение режима отладки шаблонов­

Параметр TEMPLATE_DEBUG в рабочем режиме тоже должен быть равен False, в противном случае система­ шаблонов­ Django будет сохранять дополнительную информацию о каждом шаблоне­ для вывода на страницу ошибок.

Реализация шаблона­ 404

Когда параметр DEBUG имеет значение True, Django отображает страницу с полезной информацией об ошибке 404. Но когда параметр DEBUG имеет значение False, происходит нечто иное: отображается шаблон­ с именем 404.html, который должен находиться в корневом каталоге шаблонов­. Поэтому, готовясь к передаче приложения в эксплуатацию, создайте этот шаблон­ и поместите в него сообщение «Page not found» (Страница не найдена).

Ниже приводится пример файла 404.html, который можно взять за отправную точку. Здесь мы воспользовались механизмом наследования шаблонов­ в предположении, что существует шаблон­ base.html с блоками title и content:

{% extends “base.html” %}

{% block title %}Страница не найдена{% endblock %}

{% block content %} <h1>Страница не найдена</h1>

<p>Извините, запрошенная вами страница не найдена.</p> {% endblock %}

Чтобы проверить, как действует шаблон­ 404.html, присвойте параметру DEBUG значение False и укажите в броузере какой-нибудь несуществую-

Подготовка приложения к развертыванию на действующем сервере

249

щий URL. (На сервере разработки вы получите такой же результат, как и на действующем сервере.)

Реализация шаблона­ 500

Аналогично, если установить в параметре DEBUG значение False, Django перестанет отображать страницы с трассировкой ошибок при появлении необработанных исключений. Вместо этого он отыщет и выведет шаблон­ с именем 500.html. Как и 404.html, этот шаблон­ должен находиться в корневом каталоге шаблонов­.

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

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd”>

<html lang=”ru”> <head>

<title>Страница недоступна</title> </head>

<body>

<h1>Страница недоступна</h1>

<p>Извините, запрошенная страница недоступна из-за неполадок на сервере.</p>

<p>Инженеры извещены, зайдите, пожалуйста, попозже.</p> </body>

</html>

Настройка оповещения об ошибках

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

Во-первых, включите в параметр ADMINS свой адрес электронной почты, а также адрес всех тех, кому надлежит отправлять уведомление. Этот параметр представляет собой список кортежей вида (name, email), например:

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