разработка мобильных приложений, веб-сервисов и корпоративных систем
ru en
+7 (812) 324-27-24, +7 (495) 641-87-24
Заказать звонок

Mingus: за что я его ненавижу

Перевод публикации Everything I hate about Mingus с DjangoAdvent

Обзор

Mingus - это небольшой проект Django, созданный в качестве эксперимента в практическом применении одного из ключевых свойств Django - реиспользуемых приложений. Mingus сам по себе не определяет модели, но на настоящий момент предоставляет возможность использовать более тридцати повторно применяемых приложений для создания полноценного проекта движка для блога. Тем препятствиям, с которыми мы столкнулись, и тем урокам, которые мы вынесли, разрабатывая это приложение, опирающееся на такое большое количество повторно применяемых приложений, и посвящена наша статья. Она посвящена нашему опыту работы с этим небольшим проектом с открытым кодом. Про потрясающие достоинства Django 1.2 мы здесь говорить не будем.

Что такое Mingus?

Mingus — это движок для блога. Процесс создания ещё одного движка для блога с нуля — совершенно рутинная работа. Mingus же создан специально для того, чтобы выполнить эту рутину для вас. Мило, не правда ли?

Итак, в чём же секрет его скромного успеха? Четыре основные причины:

  1. Концепция проста - движок для блогов.
  2. Концепция интересная - использовать только повторно применяемые приложения для обеспечения работы всего функционала (ведение блогов, комменарии через Disqus, управление кадрированием изображений, вставки, WYSIWYG редактор, отладка, форма обратной связи и т.д.)
  3. Позволяет разработчикам научиться использовать в своих проектах повторно применяемые приложения, даёт возможность форсированного знакомства с virtualenv и pip.
  4. Система шаблонов минималистичная и привлекательная

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


mkvirtualenv myblog —-no-site-packages
workon myblog
cdvirtualenv
git clone git://github.com/montylounge/django-mingus.git
cd django-mingus/mingus
pip install -r stable-requirements.txt
cp local_settings.py.template local_settings.py
./manage.py syncdb
./manage.py loaddata test_data.json
./manage.py runserver

Уточню, что упомянутые выше команды mkvirtualenv, workon, и cdvirtualenv требуют, чтобы был заранее установлен virtualenvwrapper. Я включил ссылку на mkvirtualenv и другие как зависимости в документации, чтобы дать разработчикам некоторое представление о virtualenvwrapper, если у них ещё нет опыта работы с этим проектом. [1] Я считаю, что это потрясающий инструмент, с которым должен быть по меньшей мере знаком каждый разработчик, работающий с Python. Чтобы познакомить нового пользователя с этим проектом, я поместил его в раздел ссылок. Если вы посмотрите на код, то увидите, что такая тактика используется довольно часто.

Теперь мы знаем - начать работать с Mingus очень легко, так что давайте приступим.

Будь «тем парнем» (или той девушкой)

Если вам доводилось запускать проект с открытым кодом, обладающий хоть каким-то потенциалом, то вы наверняка получали запросы о функционале различного рода. В конце концов у вашего проекта обязательно объявляется такой пользователь, который уже использует проект, но которому нужны ещё какие-то дополнительные функции. Такие запросы в конечном итоге оказываются у вас в почтовом ящике в списке запросов.

Именно в этот момент вам как руководителю проекта нужно принять определённые жёсткие решения. Находятся ли заявленные запросы в пределах цели вашего проекта? Помогают ли они как-то дополнить ваш проект? Или же эти запросы всего лишь удовлетворяют прихоти одного единственного пользователя? Умение вовремя сказать "нет" на запрос — ключевой навык для успешного управления любого проекта. Бывает, однако, что запрос несёт в себе рациональное зерно, и в этом случае стоит к нему прислушаться и добавить ещё один пункт в список дел на ближайшее время.

Итак, вы хороший руководитель проекта, и вы исправили ошибку или добавили новую функцию в проект. И если вы действительно заботитесь о качестве проекта, проводя рефакторинг и ежедневно работая с собственным проектом [2], то вы начнёте думать как «тот парень», и начнёте видеть пути усовершенствования своего проекта. Таким образом, вы проводите больше времени, добавляя те новые функции, которые считаете разумными и отвечающими фокусу проекта. Это могут быть и те функции, которые вы вначале просмотрели, или от которых просто отмахнулись.

Я ненавижу этого парня, поскольку он загружает меня дополнительной работой, но я люблю его, потому что он учит меня быть более профессиональным разработчиком, и он помогает мне сделать мой проект лучше. Он мой «контроль качества». Он собирает требования для моего проекта. Mingus не добился бы такого успеха, если бы не «этот парень».

Мой вам совет — станьте этим парнем (ну или девушкой), а я пусть вас за это возненавижу.

Не заставляйте меня перезагружать Apache

Если в Django и есть функция, которой недостаёт администратору, то это, на мой взгляд, управление настройками в масштабах всей системы.

В работе практически с каждым проектом я сталкивался с этим запросом: добавить возможность управлять настройками приложения на правах администратора. И неважно, это проект Django или проект ASP.Net — любому администратору необходима возможность управлять настройками в масштабах всего приложения с помощью интерфейса администратора. Но в настоящий момент в Django нет какой-то сложившейся удачной практики в этом направлении. Нам нужна возможность такого управления, и она должна быть реализована через интерфейс администратора.

Ну, в действительности там есть некоторые успешные наработки, но они в конечном итоге стали просто больным вопросом. «Best practice» стало размещение настроек приложения в файл settings.py вашего проекта. Вот пример того, как наилучшим образом получить значение опции:


from django.conf import settings
post_list_count = getattr(settings, "post_list_count", 20)

В приведённом выше примере для извлечения значения post_list_count используется getattr. По умолчанию будет присвоено значение 20. Это потрясающе, и это соответствует лучшим традициям «приемлемых значений по умолчанию».

Рассмотрев этот пример, теперь допустим, что для каждого приложения в вашем проекте (скажем, для Mingus) необходимо определить настройки в приложении в settings.py. В случае с Mingus это потребует задать 30 дополнительных значений в settings.py. Это само по себе не катастрофично, но может привести к чрезвычайному усложниению файла settings.py, тогда как мы стремимся минимизировать наши файлы настроек, не правда ли?

Следуя этим принципам, при необходимости изменить значения настроек, разработчику или администратору требуется доступ к серверу, чтобы самостоятельно обновить файл settings.py. Кроме того, кто бы ни обрабатывал запрос на изменение, ему потребуется перезагрузить сервер и веб-приложение, чтобы изменения вступили в силу. Не идеальное решение.

К счастью для нас, есть два повторно применяемых приложения, призванные решить именно эту задачу:

  1. Django-DBSettings
  2. Django-LiveSettings

Не знаю, которое из двух, но одно из этих решений (или что-то аналогичной концепции ) должно стать общепринятым решением, а в идеале — утверждённым приложением.

Конечно, есть такие простые значения параметров, как например:


BLOG_PAGE_SIZE = 20

Есть и более продвинутые:


DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.version.VersionDebugPanel',
'debug_toolbar.panels.timer.TimerDebugPanel',
'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
'debug_toolbar.panels.template.TemplateDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel',
)

Какое решение здесь возможно? Помимо простого размещения ключ/значение, в каждом приложении может также быть обработчик. В Django, конечно, есть обработчики по умолчанию, такие как IntegerSettingHandler, StringSettingHandler, и т. д. Но в приложении типа django-debug-toolbar будет обработчик DebugToolBarHandler. Это позволит приложению Настройки иметь стандартный API, через который любое приложение сможет связываться с ним через API Настроек, но каждое отдельное приложение предоставляет логику своего собственного обработчика для выполнения у себя своих правил. Или я просто псих?

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

Сейчас, если я собираюсь добавить в Mingus новое приложение, я стараюсь помнить о потребностях нетехнического конечного потребителя. Захотят ли они иметь возможность изменять эти настройки через панель управления? И если да, и если повторно применяемое приложение определяет запрашиваемое значение settings.py, мне нужно принять непростое решение. Надо ли здесь создать fork приложения, и добавить в модель настройки, которые можно обновлять через панель управления администратора [3], или же надо отказаться от этой идеи, в том числе и от сохранения других значений в settings.py проекта?

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

Можно получить это с помощью этих настроек. Или других настроек.

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

В Mingus я создаю два файла настроек:

  1. settings.py
  2. local_settings.py

Первый файл позволяет управлять настройками приложения в целом. Второй позволяет разработчику переопределить различные настройки на своем компьютере и позволяет на различных этапах своего окружения (разработка, промежуточные этапы, производство) создавать собственные файлы local_settings.py, определяющие специфические параметры настроек окружения (продумывайте настройки базы данных, файловой системы и отладки).

Я неоднократно сталкивался с такой концепцией, работая с другими проектами, и в итоге остановился на этом простом паттерне.

Но это еще не окончательный ответ. В сообщении "Усовершенствование локальных настроек"_ в своём блоге Дэниэль Линдси (Daniel Lindsey) предлагает одно конкретное решение. Но если вы заглянете в комментарии к этому сообщению, вы увидите несколько альтернативных решений. Всё это подчёркивает необходимость введения общего стандарта. На самом деле, популярный подкаст DjangoDose в Handling Development, Staging, and Production Environments предложил своё решение с использованием концепции FLAVOR. Но опять же, если взглянуть на наиболее удачные из примеров в комментариях, то мы увидим целый букет альтернативных решений, применяемых другими разработчиками.

Один из разработчиков Mingus предложил мне взглянуть на довольно любопытную документацию команды Transifix Using a list of conf files , посвящённую тому, как они довольно успешно справились с управлением файлами настроек. Уже сам тот факт, что в принципе существует статья в википедии, посвящённая различным способам управления файлами настроек, подчёркивает необходимость введения стандартов в этом направлении.

Недавно я нашёл один проект, Django-Config (разработчики - Ноуэлл Стрит, Шон Райдер, сейчас поддержку осуществляет Тэррек Хоссейн.) Оба они, Стрит и Райдер, работают в PBS, и недавно подробно разбирали сложности, с которыми сталкиваются разработчики различных проектов и повторно применяемых приложений в инфраструктуре в своей презентации на конференции DjangoCon2009 Pluggable, Reusable Django Apps: A Use Case and Proposed Solution.

Django-Config определяет самого себя как "... простой способ поддерживать несколько конфигураций для Django. Он основан на концепции, включающей в себя создание общего конфигурационного файла, при этом конфигурационный файл для каждого пользователя (или для каждого сервера) сочетает в себе базовую и пользовательскую конфигурацию и загружает её." Самому мне ещё только предстоит запустить такой проект, но, учитывая сложность поддерживаемой в PBS инфраструктуры, я верю, что где-то там в недрах уже есть скрытые самородки, проверенные и доведённые до совершенства.

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

Статичные данные? Неужели!

Любой, кто когда-либо создавал в Django повторно применяемые приложения, задавал себе вопрос: где разместить статичные данные? Какое название задать для директории? Назвать директорию static или назвать её media? Где именно расположить её в файловой системе?

Прекрасным примером является повторно применяемое приложение Симона Уиллисона django-cropper, которое я недавно интегрировал в Mingus. Уиллсон недавно оставил commit message: «Наконец получилось сделать так, чтобы пакет включал в себя шаблон... И тем не менее я понятия не имею, что мне делать с зависимостями от статичных данных.» Это хороший вопрос. Что делать разработчику с зависимости от статичных файлов? Включаются ли файлы в проект? Или же надо инструктировать пользователя, что эти данные загружаются с XYZ? И если файлы включены в проект, то где они располагаются в самом приложении?

Я считаю, что ответом здесь служит Django-StaticFiles.

Проект уходит корнями к проекту Pinax, в рамках которого разработчики столкнулись с теми же сложностями, но в гораздо большем масштабе. Так что если кто-нибудь и знает решение, так это команда разработчков Pinax. Я не собираюсь сейчас погружаться в тончайшие нюансы этого проекта, в огромную систему его специфических свойств и функций. Но в процессе реализации этого проекта вырисовывается достаточно легко перенимаемый стандарт для повторно применяемых приложений и управления статичными данными. Может быть, именно это и должно стать стандартным приложением, или по крайней мере каким-то общепринятым, традиционным решением, которого мы все так ждём?

Загрузите это

Раз уж мы заговорили об управлением данными, поговорим и о загружаемых посредством панели управления Django файлах. Я считаю, что здесь тоже должно быть какое-то традиционное общепринятое решение - директория /uploads/ внутри MEDIA_ROOT. Слишком часто, начиная работу с приложением, я вижу такое:


photo = models.ImageField(upload_to="/images/")

Это вообще не работает. Общепринятым по умолчанию должно стать решение MEDIA_ROOT +" /uploads/APP_NAME/ " и любые директории, определяемые параметром "upload_to", присоединяются директории по умолчанию, как, например, в моём приложении Photobooth:


photo = models.ImageField(upload_to="images")

По умолчанию результат загрузится в директорию MEDIA_ROOT + /uploads/photobooth/images/.

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

Никто не уважает i18n

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

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


from django.utils.translation import ugettext_lazy as _
...

class Post(models.Model)
description = models.TextField(_('description),
help_text=_('The description of your post'))

class Meta:
verbose_name = _('post')
verbose_name_plural = _('posts')
...

И всё, что надо теперь сделать в шаблонах для вывода текста, это следующее. Например, для landing.html:


{% load i18n %}

{% trans "Blog roll" %}


Вот и всё. Как водится, здесь тоже есть свои подводные камни, поэтому настоятельно рекомендую читать документацию с описанием всех необходимых вводных для начала работы, однако для большинства задач приведённой здесь информации на 80-90% будет достаточно.

Если планируете создать многоязычное приложение, вам будет интересно взглянуть на эти приложения:

Взгляните также на эту прекрасную статью, в которой приведён обзор ряда повторно применяемых приложений, облегчающих интеграцию в i18n - Dynamic Translation Apps for Django. Дело в том, что это очень, очень плохо, не переводить ваше приложение на другие языки. Нет, серьезно, если вы не интернационализируете ваше приложение, вы создаёте головную боль другим разработчикам, и, что ещё более важно, вы ещё и ограничиваете потенциальное дальнейшее распространение вашего проекта. Так что ведите себя хорошо, и обучите этого плохого мальчика другим языкам.

Всё дело в миграции

Во всех повторно применяемых приложениях, которые мы публикуем, мы должны начать использовать South! Или нам нужно чтобы разработчки Django удовлетворили мой pony request. В своём сообщении South and Reusable Apps я упоминал об этом, но сейчас я хочу ещё раз поднять вопрос актуальности общего выбора инструмента миграции.

В статье Reusable Apps in Django Panel на DjangoDose мы говорили о том, что в настоящий момент лучшим инструментом для миграций себя показывает South . Поскольку управление миграцией в принципе довольно редко практикуется в большинстве приложений, то и нет простого способа для миграции пакета приложений. И опять, винить в этом нужно именно меня. Но теперь с этим покончено. Мы двигаемся вперёд, и теперь все ставки делаем на South.

South действительно может упростить это всё для нас, разработчиков. Именно это предложение я сделал в вышеупомянутой публикации в блоге. Андрей Гудвин, автор South, отметил, что решение для мигрирующих повторно применяемых приложений, которые сами по себе не используют South, уже в работе. Это решение ожидается к выходу в ближайшей версии South. То есть, ещё не вся надежда потеряна. Эта функция позволит разработчикам создавать миграции South для повторно применяемых приложений, даже если в самих этих приложениях South не используется вообще. Потрясающе!

В данный момент Mingus предоставляет только один тип миграции (raw SQL), но даже и его не было до недавних пор, когда Mingus стали использовать в качестве движка для создания блогов. Зная это, было бы небрежностью с моей стороны не предоставить возможность использовать миграции для пользователей, которые с нетерпением ждут выхода нового релиза Mingus. Пока что у них для этих задач есть только raw SQL, но это совсем не то, что нужно. А нужно тут применять стандартные инструменты миграции, которые мы все и применяем.

Ключи кэша правят миром

cache framework Django чрезвычайно функционален и гибок. Но одну вещь я постоянно слышу от разработчиков снова и снова: необходимо убедиться, что ключи кэша правильно названы, чтобы избежать конфликтов. Необходимы уникальные ключи кэша, которые легко могут быть вызваны для запросов, для проверки целостности/недействительности кэша и т.д.

Так почему не завести здесь какого-нибудь помощника, упрощающего эти задачи? Именно это я и сделал, добавив create_cache_key в django-sugar. Метод на самом деле совмещает в себе код одного сообщения блога и повторно применяемое приложение:

Вот, посмотрите на API:


from blog.models import Post
slug_val = 'some-slug'
mykey = create_cache_key(Post, 'slug', slug_val)
obj = cache.get(mykey)

В приведённом выше примере create_cache_key принимает в качестве первого аргумента Model или Manager, в качестве второго аргумента - интересующее вас поле, и значение поля в качестве третьего аргумента. На базе этого он может генерировать и регенерировать ключ кэша. Преимущество здесь в том, что он изолирует логику, чтобы сохранить имена ключей кэша. Он управляет этой конструкцией для вас.

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

Заключение

Django - вот причина, почему я так люблю придираться к Mingus. Я люблю Django и Python. И на самом деле это моё негодование - всего лишь маленькое пятнышко на потрясающе приятном путешествии в компани с Django.

В будущем будет выпущена финальная версия Mingus 1.0, в которой будут исправлены ошибки, добавлено ещё больше документации, и ещё больше тестов. Мало что ещё можно добавить к тому, что по сути является концепцией проекта, так что я думаю, что текущий набор функций будет окончательным.

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

Кевин Фриковски - основатель Monty Lounge Industries, LLC, где он также ведёт блог про Django, про веб-разработку, дизайн и про юзабилити. Он состоит в команде DjangoDose, и является одним из основателей Django-NYC. Свои репозитории ПО с открытым кодом он поддерживает на GitHub. Его твиттер - @montylounge. У него двое детей и очаровательная жена. Что касается работы, он глубоко уверен, что ему просто повезло, что приходится работать с Python и Django.

[1] Even if it's an itsy bitsy one.
[2] Since my docs reference these, I assume that by following the docs you forced yourself to play with these excellent tools.
[3] Eating your own dog food.
[4] At this point it's arguably no longer reusable, or actually maybe more reusable now that I think about it.

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

Комментарии

  • Руслан 2 года назад

    спасибо за перевод. интересно.

    кстати, прошу сделать буквы ещё мельче, что бы читать было ещё комфортнее.

    и капчу ещё более не читаемую, что бы никто не мог комментировать.

  • zzxzxc 2 года назад

    Я бы ещё порекомендовал убрать вступление про "Перевод публикации", чтобы никто не мог даже в гугле найти оригинал, не то что по прямой ссылке отсюда перейти.

    Алсо, больше красного текста на сером фоне!

  • Анатолий Ларин 2 года назад

    Ребята спасибо за отзывы!

    По поводу оригинальной статьи http://djangoadvent.com лежит, потому не могу дать ссылки на оригинал :(

    Вот исходники статьи:

    http://github.com/djangoadvent/djangoadvent-articles/blob/master/1.2/05_everything-i-hate-about-mingus.rst

    =====
    По поводу дизайна: ВЫ АБСОЛЮТНО ПРАВЫ

    Мы пытаемся убедить дизайнеров, но программисты пока сдают :)

  • sergej 2 года назад

    Thank you!

  • Евгений 2 года назад

    Ужасный перевод, читать невозможно, кругом дословная калька с английского. Хорошо хоть есть название оригинальной статьи, прочёл оригинал. Кстати, djangoadvent.com уже не лежит, можно поставить ссылку на оригинал (http://djangoadvent.com/1.2/everything-i-hate-about-mingus/).