Заголовок поста задуман ироничным, потому что никакой более конкретной темы у поста нет :-). Потому что планировал я его про одно, но Pythy написал свою подначку, и я решил вытащить шашку наголо и защитить любимый фреймворк! И вот, чтобы не пускать два связанных поста один за другим, я их соптимизировал.
Растем!
Итак, сначала новость. Джанговцы наконец собрались и привнесли в свой до недавней поры "домашний" опенсурс-проект некоторое количество порядка, которого ему не хватало. "Домашность" проявлялась в том, что с того момента, как разработчики открыли код, у них не было вообще никакого процесса, как им обслуживать кучу разработчиков, которая начинает искать баги, писать патчи и всячески спамить Trac (хе-хе :-) ).
Пока разработчики, непосредственно меняющие код (коммиттеры), справляются с потоком собственными силами, все хорошо. Но когда патчей становится больше, появляются "висяки" (патчи, про которые все забыли), новые странные баги (от патчей недосмотренных и включенных впопыхах), а также обиженные разработчики ("почему мой патч не включают?") и флейм-войны в списке рассылке ("почему вы меня игнорируете!" — "а вам никто не обязан..."). Дальше у коммиттеров обычно наступает фаза фрустрации, когда тикетов настолько много, что "хуже уже не будет", они забивают на остальных и занимаются тем, что интересно лично им. Опенсурс на это момент обычно или дохнет совсем или, если кому-то сильно интересен, форкается в новый проект.
Но кажется, с Django такого не будет. Как я уже сказал, народ собрался и придумал четкий процесс обработки входящих тикетов, назначил ответственных за их изучение и назначение всяких организующих флажков. И процесс пошел! В частности, уже на два моих давно забытых патча обратили внимание, и один из них пометили готовым к включению.
{% url %}
И вот один из эти патчей — реализующий тег {% url %}
— плавно подводит к одному из моментов, которым недоволен Pythy:
Да, есть способ, однако в стандартной библиотеке тегов Django нет тега {% url %}. В итоге есть несколько реализаций {% url %}.
И вот с этим, как я надеюсь, упомянутый процесс справится. Однако это не самая большая претензия, как я понял, а хуже то, что этот тег по мнению Pythy:
Не удобен тем, что нужно указывать низкоуровневый параметр, непосредственно передающийся в view. Захочу я поменять схему URL - и мне придется в каждом месте, где я делал reverse url lookup, изменять передаваемый параметр. А я хочу чтобы передавался объект и я бы указывал в одном месте, как объект обратно резолвится в URL.
Вот тут позволю себе потеоретизировать.
Во-первых, есть общая ловушка, в которую попадали все предыдущие попытки придумать обратное разрешение URL'ов: они искали способ разрешения объекта в URL. Объекта модели данных, в смысле. Но штука в том, что веб-приложение состоит не только из доступа к страницам объектов. Есть еще страницы, которые не описываются никакими моделями данных: страница поиска, страница логина, страница смены пароля... Другими словами, в рамках архитектуры в Django, поскольку URL в общем случае показывает на view, а не на модель, и обратное разрешение должно делать из view, а не из модели, последнего недостаточно.
Во-вторых, после того, как мы разберемся с базовым тегом {% url %}
, я хочу придумать его аналог для generic views (с которыми он сейчас не работает), который будет принимать именно объекты моделей. И как раз это должно решить проблему "URL объекта" в большинстве случаев, потому что такие URL'ы обычно именно generic'ами и обрабатываются.
Ну и в-третьих, я не очень согласен с тем, что то, что передается в тег {% url %}
— это низкоуровневые параметры. Они ни фига не низкоуровневые, потому что они используются в публичном интерфейсе сайта. И у хорошего сайта схема URL должна быть в идеале вечной, или по крайней мере, очень редко меняющейся. Поэтому аргумент "захочу я поменять схему URL" я бы не рассматривал в качестве большого минуса. Если такая нужда возникнет, причем такая, что изменения будут не косметические, а затронут параметры, такие как id и slug'и объектов, то по-хорошему надо все равно оставлять за собой набор правил для совместимости, чтобы поддерживали старую схему.
urlconf на регулярках
Вот что в первую очередь мешает в Django - это urlconf на регулярках
Честно скажу, прочитал — крайне удивился. Уж что-что, а это лично мне всегда казалось очень здравой и удобной идеей. Поэтому не знаю, правильно ли я понял саму претензию... Но тем не менее.
Мне кажется, что если говорить о как таковом наличии отдельного маппера между URL'ами и логикой, то проще регулярок тут ничего не придумать. Они и универсальны, и достаточно просты в тех случаях, в которых реально используются в urlconf. Зачем городить что-то новое? Я серьезно считаю, что это "в доску" просто:
(r'^client/(\d+)/profile/$', views.client)
Еще есть мнение, что маппер вообще не нужен. Что URL можно составлять автоматически из названий объектов и методов, как это сделано в CherryPy и, если я не отстал от жизни, RoR. Но вот это, на самом деле, зло. Во-первых, потому что связывает схему URL с внутренней структурой софта и не позволяет ее свободно менять. А во-вторых, потому что не дает создать URL'ы, четко соответствующие пользовательской модели программы, а вместо этого выставляют наверх модель ее реализацию, которая пользователю чужда и, соответственно, смысл URL как одного из инструментов пользовательского интерфейса теряется.
Проекты-приложения
Завершающие движения моего сегодняшнего ката с шашкой посвящены разделению понятий проектов и приложений:
либо ты нагружаешь приложение "не тем" функционалом (теряется атомарность), либо приложения зависят друг от друга (теряется изолированность). Мое мнение таково, что проект - лишнее понятие. Проект должен быть таким же приложением.
Проект в Django несет вполне определенную функцию (которая, что верно, плохо понятна из официального учебника). Это хранилище настроек и дизайна (веб-дизайна) отдельного мнэ-э-э... проекта на сайте. Но он — джанговский проект — сам по себе не несет никакой программной логики. Вся логика лежит в приложениях. А вот приложения бывают разные. Конкретно они бывают двух явно выделяющихся типа:
- самодостаточные приложения
- приложения-библиотеки
Самодостаточные приложения — это например "давайте сделаем социальный веб-сервис закладок". Весь сайт посвящен этим закладкам, и все делается в одном приложении. Такие приложения, конечно, и не предполагается никуда переносить-встраивать.
Приложение-библиотека — это, скажем, отдельный форум, middleware со статистикой запросов, TagsField, наконец. То есть то, что действительно можно скачать откуда-нибудь и прикрутить в проект.
Так вот, обычно все непонятности с разделением функций между приложением и проектом связаны именно с первым типом. Потому что, если весь сайт — одно большое приложение, то зачем нужна еще какая-то обертка-проект? Я скажу, зачем. Для унификации. В Django всегда есть проект, в который можно класть приложение. Чтобы когда вам вдруг захочется добавить в ваш монолитный сайт что-то внешнее, не надо было бы на ходу отделять от приложения оберточную часть с URL'ами, шаблонами и паролями к БД. Она уже есть.
Помню, для меня это разделение стало очевидным после того, как Адриан Головатый рассказал, что у них в World Online приложения вообще не лежат внутри директорий проектов. Есть какое-то отдельное Django-место, в котором хранятся приложения, и по мере необходимости они включаются в проекты, которые представляют собой разные сайты. То есть это совсем разные вещи.
Матэ!
Комментарии: 20
вот скажу только одно. url-маппер в django (в смысле "урлы на регулярках") мне НЕВЕРОЯТНО нравится. Нигде лучше, удобнее, проще и понятнее не видел.
В RoR по умолчанию урлы составляются по названиям контроллеров, видов и моделей, но мапить потом можно как хочешь, насколько я знаю.
Хотя то, как это реализовано в Django, мне нравится гораздо больше.
Нет, под объектом понимается не объект модели данных. Для пояснения:
Пример того же блога, где у архива имеется URL вида
/blog/YYYY/MM/DD
, так вот хотелось бы в {% url %} передавать не отдельно год, отдельно месяц, отдельно день, а объектdatetime.date
.Насколько я знаю принципы MVC, обращение по какому-либо url всегда вызывает какой-нибудь controller, но никак не view изначально. А вот уже потом... Но это - только потом.
В RoR по умолчанию маппинг именно на имя класса контроллера, метод и параметр id, но можно это дело раскатать куда проще:
после чего во всех шаблонах и контроллерах автоматически работает резолюшен в обратную сторону
превращение конкретного обьекта в "компонент URL" реализуется скрытым методом to_param, который в каждом классе можно определить отдельно. К примеру так:
и параметром этой страницы станет транслитерированный заголовок
@TeXHaPb:
в терминах Django — view - это контроллер, а template - вид/представление. Так что в данном контексте под view понимается именно Django view, т.е. контроллер в MVC.
"в терминах Django — view - это контроллер"
Зачем авторам было делать во фреймворке такую путаницу? Назвали бы контроллером, если это контроллер.
Потому что это не четко контроллер, как он понимается в MVC: http://www.djangoproject.com/documentation/faq/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names
Вопрос есть один по Django. А есть ли в Django аналог рельсовых плагинов?
Pythy на мой вопрос на конференции rupy-omsk больно как то уж невнятно ответил на этот вопрос.
Я, к сожалению, не в курсе, что такое эти плагины. Расскажете вкратце?
Спектр того, что могут делать плагины в рельсах, очень широк: от добавления хелперов и/или нового функционала в модели(acts_as_taggable, file_column, acs_as_veraioned, acts_as_audited) до добавления новых типов шаблонов, готовых к использованию(например markaby)
Понял. В Django для этого нет одного названия, это существует в виде разных мест, где могут работать дополнительные компоненты:
А в принципе, вся архитектура очень открытая, и дополнять ее можно в очень разных местах.
Еще забыл одно приметное место — пользовательские теги для шаблонов.
Спасибо за подробное объяснение.. Совсем забыл про разделение "проекты/приложения" в Django.
Впринципе джанго-приложения в большей степени можно наверное сравнить с рельсовыми плагинами..
Надо будет конечно поподорбнее самому все изучить/пощупать..
В рельсах есть возможность установки плагинов из командной строки:
script/install plugin_name
можно также задать SVN-репозиторий, откуда забирать плагин..
Есть ли такое в Django?
Всмысле необязательно, чтоб прям из командной строки устанавливалось, а просто имеется ли какая-то централизованная база таких приложений.. Смысл в возможности повторного использования (чужого) кода.
В Rails-community считается почетно написать плагин, и выложить его в общий доступ. Есть ли такие тенденции в Django?
Угу. В общем-то, вся дополнительная функциональность обычно в виде приложений и оформляется. Даже если это, скажем, просто один шаблонный фильтр.
Скрипта нет. Приложение помещается куда-нибудь в питоновский путь (чаще всего внутрь проекта, но можно и отдельно держать где-нибудь, если удобно) и прописывается в настройках в INSTALLED_APPS. Единственно, нигде нет официального репозитория, все валяется по блогам, викам и закладкам...
Конечно :-). Выкладывают, и в виде оформленных пакетов, и в виде репозиториев.
Ок, понятно, спасибо еще раз за ответы
{% url %} забрался в svn. ура!
Ура :-). Одной папочкой с патчем меньше...
Нашел Ваш блог в гугле по django, добавил в RSS-ридер, так-как сейчас активно перехожу с php на python и умные мысли очень помогут.
Пишите еще :)
Уже года четыре исключительно использую контроллеры на регулярных выражениях (PHP). Весьма доволен. Единственная проблемы возникла, когда администратору дали возможность самому составлять дерево сайта. Решил так: написал подконтроллер, и когда мои RE по запросу пользователя ничего не находили передавал этот запрос подконтроллеру, который совещался с деревом и ещё раз обрабатывал запрос. Получилось дерево наследования контроллеров, где основная часть кода в корне дерева (FrontendController) одинаковая, разница (class TreeBasedFrontendController) только в одном методе: get_current_view. Интересно, а как на Django такое сделать?