Интересно, что когда рекламируют почти любой фреймворк (не только для веба), обязательно подчеркивают, как замечательно он позволяет решать задачи без написания строк кода (как Дэвид Хайнемайер Хансон в презентации RoR: "Look at all those things I'm not doing..." (когда у Django появится скринкаст, думаю, там тоже будет что-нибудь такое (интересно, а позволяет русский язык использовать тройные вложенные скобки?))).
Насколько я понимаю, в то, что существует фреймворк, позволяющий делать серьезную систему одними только настройками конфигов из красивого GUI, верят только совсем оторванные менеджеры, которые, видимо, ненавидят своих програмистов, и только и думают о том светлом дне, когда можно будет от них всех избавиться (откуда, интересно, эта классовая ненависть?) Остальные, думаю, согласятся, что реальные проекты обычно несколько отличаются от тьюториалов по функциональности, и фреймворк может только лишь оказывать посильную помощь в ее программировании в большей или меньшей степени. И мешать, кстати, тоже :-).
Недавно Django сильно продвинулся в направлении "помогать".
Generic views
Views в Django — это функции, которые принимают запрос с веба, что-то с ним делают и формируют набор данных для шаблона. То есть, то место, где делается пресловутая "бизнес-логика". Часто бывают такие ситуации, когда никакой особенной логики для выдачи данных просто нет. Например, если нужно показать на странице содержимое заказа в интернет-магазине, вся логика заключается в выборе объекта заказа из базы:
def order(request, id):
return render_to_template('order', {'order':orders.get_object(pk=id)})
И этот код повторяется из функции в функцию, с заменой слова "order" на другое. Для таких частых и тупых задач в Django придуманы "generic views" — уже написанные шаблонные функции, которые просто достаточно подключить к вашим URL'ам. Сейчас они есть такие:
- показ объекта
- показ списка объектов (с постраничным выводом)
- показ архивов сгруппированных по дате
- добавление, изменение и удаление объектов
- простой вывод шаблона с переданными данными
Различные параметры, как например название и id объекта, который надо показывать, скармливаются там же, в конфиге URL'ов:
(r'^orders/(?P<object_id>\d+)/$',
'django.views.generic.list_detail.object_detail',
{'app_label':'internet_shop', 'module_name':'orders'}),
Вот такая строчка в конфиге (разбил на три для красоты) — это единственный код, который нужен для того, чтобы по URL'у "orders/1211/" вывелось содержимое заказа 1211.
Тут мне хочется особенно отметить, как соблюден баланс между "помогать" и "не мешать". Эти функции делают именно простые муторные вещи, которые все равно бы рано или поздно в любом проекте выделились в отдельные функции (в лучшем случае), а то и делались бы просто copy-paste'ом. Но они не делают лишнего. Например, они могли бы для своего вызова навязывать определенные URL'ы или определенные шаблоны, и тогда бы пришлось больше времени тратить на нивелирование всяких ненужностей.
Штука действительно удобная. Вот, некто Росс Пултон блог свой написал на Django в качестве пробного проекта, и рассказывает, что вообще не написал никакого кода! И я верю, потому что все, что нужно в блоге, в generic'ах есть.
Но так шоколадно в жизни, конечно, не бывает :-). И никакого кода у Росса нет, потому что блог очень простенький. А вот когда проект начинает потихоньку обрастать функциональностью, тогда начинаются сложности.
Контекст шаблона
Я на такие сложности натолкнулся почти сразу и, честно говоря, поначалу этими generic views совсем не проникся. Больше того, я даже недоумевал, зачем их вообще рекомендуют использовать, потому что до недавнего времени они годились только для действительно базовых вещей. И вот почему.
Дело любой view-функции — вытащить из базы, пересчитать и как-то подготовить переменные, которые потом передаются в шаблон для отображения. Эти данные называются контекстом шаблона и представляют собой набора пар "имя:значение". Очень простая штука.
Но обычно на сайте на многих страницах присутствует какая-то повторяющаяся информация: текущий залогиненный юзер, какая-нибудь история переходов, корзина с товарами. Данные для всего этого тоже передаются каждой странице в контексте. И чтобы не заполнять контекст этими одинаковыми данными в каждой view-функции, это надо вынести куда-то отдельно. Самый очевидный вариант — это создать свой объект контекста, отнаследовав его от стандартного, и в конструкторе заполнять его нужными для своего сайта постоянными значениями. Этот способ и рекомендовался документацией.
Но ведь generic views — библиотечные функции — понятия не имеют о вашем новом классе контекста. Они создают контекст самостоятельно, и нет никакой возможности подсунуть им свой. И получается, что generic views работают только тогда, когда вам в странице нужно только то, что было предусмотрено в Django.
Надо, правда, сказать, что в Django предусмотрено, в общем-то, немало. Например, самая очевидная структура — залогиненный юзер — в Django есть стандартная, и generic views ее знают. Но вот мне, например, в проекте стандартный юзер не подошел, да и еще пару параметров из запроса надо было передавать...
В общем, пользоваться generic views у меня не получалось. Наверное можно было что-то подхачить, но это выглядит как слишком много усилий, чтобы заставить фреймворк мне "помогать".
Решение — обработчики контекста
Но все в одночасье изменилось :-). Недавно в Django появились "обработчики (процессоры) контекста". Это, по сути, пользовательские функции, которые вызываются после создания контекста и отдают любые дополнительные данные, которые в него вливаются. Список функций задается в настройках проекта.
Собственно, это решает всю проблему. Теперь я могу отрефакторить весь свой код (вот забава!), использовать generic views, а те несчастные две переменные, которые мне нужны в контексте почти всегда, свалить в такой вот процессор.
Комментарии: 3
http://www.livejournal.com/users/hodzanassredin/25264.html#cutid1
Вот еще немног инфы про Django для начинающих. Перевод туториала часть 1 (коряво с ошибками)
В Rails ты просто создаешь шаблон который называется как вызов действия в контролллере (типа show.rhtml) и не пишешь def show; end; - он автоматически проваливается к шаблону.
Написал маленький "Getting Started" про CakePHP, другой фрейморк с иделологией MCV
Печем пирожки или начало работы с CakePHP по-русски
Всем советую поковырять этот фреймворк )