Заголовок поста задуман ироничным, потому что никакой более конкретной темы у поста нет :-). Потому что планировал я его про одно, но 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

  1. Сергей Петров

    вот скажу только одно. url-маппер в django (в смысле "урлы на регулярках") мне НЕВЕРОЯТНО нравится. Нигде лучше, удобнее, проще и понятнее не видел.

  2. Mourner

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

    Хотя то, как это реализовано в Django, мне нравится гораздо больше.

  3. pythy

    Нет, под объектом понимается не объект модели данных. Для пояснения:

    Пример того же блога, где у архива имеется URL вида /blog/YYYY/MM/DD, так вот хотелось бы в {% url %} передавать не отдельно год, отдельно месяц, отдельно день, а объект datetime.date.

  4. TeXHaPb

    Другими словами, в рамках архитектуры в Django, поскольку URL в общем случае показывает на view, а не на модель, и обратное разрешение должно делать из view, а не из модели, последнего недостаточно.

    Насколько я знаю принципы MVC, обращение по какому-либо url всегда вызывает какой-нибудь controller, но никак не view изначально. А вот уже потом... Но это - только потом.

  5. Julik

    В RoR по умолчанию маппинг именно на имя класса контроллера, метод и параметр id, но можно это дело раскатать куда проще:

    map.person "/profiles/:id", :controller => "people", :action => "edit"
    

    после чего во всех шаблонах и контроллерах автоматически работает резолюшен в обратную сторону

    <%= link_to "Мой профиль", person_url(@person) %>
    

    превращение конкретного обьекта в "компонент URL" реализуется скрытым методом to_param, который в каждом классе можно определить отдельно. К примеру так:

    class Page < ...
       def to_param
           name.translify
       end
    end
    

    и параметром этой страницы станет транслитерированный заголовок

  6. pythy

    @TeXHaPb:

    в терминах Django — view - это контроллер, а template - вид/представление. Так что в данном контексте под view понимается именно Django view, т.е. контроллер в MVC.

  7. 19th

    "в терминах Django — view - это контроллер"
    Зачем авторам было делать во фреймворке такую путаницу? Назвали бы контроллером, если это контроллер.

  8. Иван Сагалаев
  9. Van

    Вопрос есть один по Django. А есть ли в Django аналог рельсовых плагинов?
    Pythy на мой вопрос на конференции rupy-omsk больно как то уж невнятно ответил на этот вопрос.

  10. Иван Сагалаев

    Я, к сожалению, не в курсе, что такое эти плагины. Расскажете вкратце?

  11. Van

    Спектр того, что могут делать плагины в рельсах, очень широк: от добавления хелперов и/или нового функционала в модели(acts_as_taggable, file_column, acs_as_veraioned, acts_as_audited) до добавления новых типов шаблонов, готовых к использованию(например markaby)

  12. Иван Сагалаев

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

    • middleware — фильтры, обрабатывающие запросы в целом, хороший пример — HTTP-авторизация, которая распаковывает логин/пароль и вешает в оговоренное место объект пользователя или поддержка If-Modified-Since/Last-Modified
    • декораторы к view-функциям — это питоновская штука, позволяющая красиво завернуть одну функцию в другую, чтобы она делала доп. операции, хороший пример — для того, чтобы конкретную страницу спрятать за авторизацией, достаточно завернуть ее функцию в декоратор login_required, или например декоратор, выцепляющий из GET-параметров номер страницы в длинных списках и передающий его уже в готовом виде в функцию
    • доп. приложения — это один из принципов архитектуры Django, что можно создавать подключаемые приожения, в качестве примера всегда привожу свой TagsField, он полностью содержит всю функциональность от модели БД до юзерского интерфейса.
    • backend'ы авторизации — например авторизация по OpenID

    А в принципе, вся архитектура очень открытая, и дополнять ее можно в очень разных местах.

  13. Иван Сагалаев

    Еще забыл одно приметное место — пользовательские теги для шаблонов.

  14. Van

    Спасибо за подробное объяснение.. Совсем забыл про разделение "проекты/приложения" в Django.
    Впринципе джанго-приложения в большей степени можно наверное сравнить с рельсовыми плагинами..

    Надо будет конечно поподорбнее самому все изучить/пощупать..

    В рельсах есть возможность установки плагинов из командной строки:
    script/install plugin_name
    можно также задать SVN-репозиторий, откуда забирать плагин..

    Есть ли такое в Django?
    Всмысле необязательно, чтоб прям из командной строки устанавливалось, а просто имеется ли какая-то централизованная база таких приложений.. Смысл в возможности повторного использования (чужого) кода.

    В Rails-community считается почетно написать плагин, и выложить его в общий доступ. Есть ли такие тенденции в Django?

  15. Иван Сагалаев

    Впринципе джанго-приложения в большей степени можно наверное сравнить с рельсовыми плагинами..

    Угу. В общем-то, вся дополнительная функциональность обычно в виде приложений и оформляется. Даже если это, скажем, просто один шаблонный фильтр.

    В рельсах есть возможность установки плагинов из командной строки:
    script/install plugin_name
    можно также задать SVN-репозиторий, откуда забирать плагин..

    Есть ли такое в Django?

    Скрипта нет. Приложение помещается куда-нибудь в питоновский путь (чаще всего внутрь проекта, но можно и отдельно держать где-нибудь, если удобно) и прописывается в настройках в INSTALLED_APPS. Единственно, нигде нет официального репозитория, все валяется по блогам, викам и закладкам...

    В Rails-community считается почетно написать плагин, и выложить его в общий доступ. Есть ли такие тенденции в Django?

    Конечно :-). Выкладывают, и в виде оформленных пакетов, и в виде репозиториев.

  16. Van

    Ок, понятно, спасибо еще раз за ответы

  17. wiz

    {% url %} забрался в svn. ура!

  18. Иван Сагалаев

    Ура :-). Одной папочкой с патчем меньше...

  19. crash

    Нашел Ваш блог в гугле по django, добавил в RSS-ридер, так-как сейчас активно перехожу с php на python и умные мысли очень помогут.

    Пишите еще :)

  20. Василий

    Уже года четыре исключительно использую контроллеры на регулярных выражениях (PHP). Весьма доволен. Единственная проблемы возникла, когда администратору дали возможность самому составлять дерево сайта. Решил так: написал подконтроллер, и когда мои RE по запросу пользователя ничего не находили передавал этот запрос подконтроллеру, который совещался с деревом и ещё раз обрабатывал запрос. Получилось дерево наследования контроллеров, где основная часть кода в корне дерева (FrontendController) одинаковая, разница (class TreeBasedFrontendController) только в одном методе: get_current_view. Интересно, а как на Django такое сделать?

Добавить комментарий