Pull to refresh

Comments 21

Код в методе модели – если код относится к конкретному инстансу модели
Код в методе менеджера – если код затрагивает всю соответствующую таблицу

Куда помещаете код, который работает с несколькими таблицами одновременно, да так, что даже одну основную из них выделить затруднительно?
Зависит от назначения кода. Если основную выделить сложно, скорее всего во вьюху, возможно в отдельный файл.
UFO landed and left these words here
Фейспалм. Следите за мыслью автора:

Логика во вьюхе — плохо.
Логика в форме — плохо.
Логика в utils — плохо.
ВНЕЗАПНО логика в моделях — хорошо.

Логика должна быть в logic.py — потому что это логика. А методы на 3 экрана в моделях — говно.

Автор говорит в основном о логике, касающейся работы с данными из моделей. Почему не разместить её в методах модели или менеджера?

> Логика должна быть в logic.py — потому что это логика. А методы на 3 экрана в моделях — говно.

А если конструктивно? Вы предлагаете всю логику писать в отдельном файле?
Слишком категорично. Возможно вы правы в случае крупных проектов, но логика бывает разная и иногда в методах модели/менеджера ей самое место.
Вообще про логику в моделях еще в Django Two Scoops (самая крутая книга по джанге на мой взгляд, ссыль) написали, но этож ад!

Приведу пример. Мы разрабатывали CRM, которое обеспечивало все рабочие процессы компании. В кратце, там был прием и обработка заявок со сложной финансовой калькуляцией, а также статистика. Было у нас все «хрестоматийно»: толстые модели с логикой, причем функции логики вызывались прямо из теплейта. Сначала все было ок. Но по мере нарастания функционала начались Содом и Гоморра =)

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

В итоге, я соглашаюсь с тем, что всю сложную логику нужно выносить в отдельные службы. Ну а в модель выносить то, что касается непосредственно этой модели, вроде get_то, set_се.

Спасибо за ссылку. Для интересующихся: там же есть издание по 1.6 (но только в бумаге, в 2х дороже и на 1.3х объемнее).
Вероятно речь идёт о бизнес-логике, которой самое место в модели.
Зря КДПВ убрали, довольно забавная игра слов была. :)
Видимо, кто-то посчитал её недостаточно толерантной)
Частая ошибка при переносе логики в модель (или utility-модуль) — передача в эти методы request-а (а порой в ответ еще отрендеренный шаблон приходит).
В таком подходе никакой инкапсуляцией и не пахнет — получается размазанная по куче файлов view с невозможностью вызывать методы бизнес-логики из сторонних приложений или тестов.
Автор об этом упоминает: «Если ваша логика завязана на объект request, то ей, вероятно, самое место в представлении.». Т.е. можно закодить как примесь для CBV, но тут уже по ситуации.
Вопрос организации больших объемов кода хорошо раскрыт в книгах по Domain Driven Design. То, что предлагает автор оригинальной статьи — архитектурное самоубийство.
Следует помнить, что вообще в джанге как таковой сервисный слой из коробки не предусмотрен, не считая middleware-ей, но у них немного иное предназначение, afaik.

Поэтому мне не кажется, что закинуть часть логики в модель это «архитектурное самоубийство», не говоря уже о том, что это никак не противоречит DDD.
Главное не забыть, что вся валидация-рендеринг и, возможно, поднятие модельных объектов должно остаться на уровне View, а модель должна работать на уровне именно модели — т.е. взаимодействовать с другими бизнес-объектами и ничего не знать о View.
> не говоря уже о том, что это никак не противоречит DDD.

Вот с этим утверждением частично не соглашусь.

Модель в Джанге — это Entity в DDD, которая при наличии ORM служит также и компонентом Repository. Если у нас есть логика, которая явно отвечает за работу с данными в репозитории, мы смело присоединяем её к модели. Примером такой логики может служить шардинг данных между разными кластерами. Стандартная практика переопределять «Model.save()» — пример такой логики.

Но вот в статье автор приводит пример с «quote.accept()». Эта практика противоречит принципам DDD, так как в определении функции мы видим несколько этапов:

— валидация запроса
— вызов стороннего компонента (braintree.Transaction.sale)
— обработка результата операции (Transaction.save() + quote.save()).

В терминах DDD это будут:
— Validation (пользовательский запрос содержит корректные данные на входе)
— Policy Check (аккаунт содержит информацию о кредитной карте)
— Service Call (braintree.Transaction.sale())
— Service Call (Transaction.save())
— Policy Check (транзакция была успешно зарегистрирована в системе)
— Repository Call (quote.save())

Если мы помещаем всю эту логику в модель Quote, то мы сразу нарушем Single Responsibility Principle, так как Quote у нас оперирует терминами Transaction и Credit Card, тогда как роль любой сущености — самоидентификация.

Поэтому я бы поместил эту логику в AcquiringService, который оперирует API из:

— AccountService — получение информации о кредитке и пользователе
— TransactionService — атомарное логгирование транзакций)
— AquiringPolicy — проверка согласованности между сущностями Account, Quote, Transaction.

В итоге получается, что сервисы общаются между собой, правила общения определяют Policies описанные в бизнес-терминах, сущности предоставляют самоидентификацию бизнес-моделей, но ничего не знают о других сущностях, а Django Views служат в качестве терминала доступа к бизнес-логике приложения через HTTP.
Советую всем, кто задается тем же вопросом, что и автор, но не считает решения автора удовлетворительным, ознакомится с ответами на мой похожий вопрос, поставленный на stackovertflow полтора года назад:

stackoverflow.com/questions/12578908/separation-of-business-logic-and-data-access-in-django
Отмечусь тут как автор django-fsm

django-fsm — хорошо служит для описания логики самой модели, и связи между полями модели, а не для того чтобы засовывать в модель все что угодно.

Если при подтверждении заказа вам надо поставить accepted_time, самое естественное место для этого — это метод модели. Если при оплате, мы цепляем транзакцию к заказу, экземпляр транзакции можно передать в метод, меняющий статус заказа. Отправленный заказ не может просто так встать на отправку опять, потому что мы по ошибке забыли где-то спрятать ссылку или злоумышленник сделал прямой POST запрос, а мы забыли проверить условие в одном из view — это тоже хорошо можно проконтролировать на уровне модели.

Пытаться сделать что-то большее, это наступать на грабли.

Я уже раза три реджектил pull request'ы от разных людей пытающихся добавить передачу параметров в pre_translation условия. Потому что единственный внятный usе-case озвученный авторами была проверка прав пользователя на совершения данной операции.

Аналогично, про попытки добавления логирования изменений. Уровень модели это не самое удачное место для этого. Те ухищрения с post_save сигналами которые пришлось применить авторам, уже явно на это указывают.
Контроллер – это встроенный в django URL-маршрутизатор, который обеспечивает логику запрос-ответ.

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

A controller can send commands to the model to update the model's state (e.g., editing a document). It can also send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document).

http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
Django URL маршрутизатор вообще ничего не знает о моделях, тем более о их состоянии.

Ссылка по теме: Django MVC или MTV.
Года два назад меня посетила мысль, что в Django явно не хватает ещё одного слоя — «бизнес-логики». Сейчас понимаю, что вопрос гораздо глубже… Те кто пишет про всякие SOLID, DDD и пр. явно правее тех, кто выбирает между толстой моделью, толстым менеджером, толстым view, толстой формой или отдельным logic.py :)

Забавно, что к пониманию многих вещей меня подтолкнуло погружение в JavaScript, и, конкретно вот эта статья на Хбре. Знаю, в среде питонистов часто бытует религиозная ненависть к яваскрипту — абстрагируйтесь, почитайте, статья больше о понимании принципов чем о языке.
Only those users with full accounts are able to leave comments. Log in, please.