Pull to refresh

Comments 12

Нету хороших или плохих фреймворков — вы просто не умеете их готовить.
Вообще прочитал статью по диагонали и зерно здравого смысла в словах автора имеется, однако если капнуть чуть поглубже то:
1- во вьюхах не должно быть обращений к бд, только валиадция, сериализация и десериализация
2- дрф хоть и позволяет делать crud легко и быстро, но так же оно размазывает бизнес логику по разным уровням приложения. Если следить очень, очень, реально очень внимательно за кодом — не проблема, но шаг в сторону и привет макароны.
3- логика в моделях. Этим страдают всякие django-fsm тут даже говорить не о чем.

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

Если оч коротко, то
вьюха принимает запрос и отдает ответ.
Дрф только валидирует запрос, (де)сериализует
Модель — тупо описание схемы бд (близко по смыслу sqlalchemy.Table)
Слой общения с бд (тот самый репозиторий)
И слой бизнес логики — по сути функция с параметрами которая принимает на вход какие-то параметры согласно контрактам (интерфейсам), общается репозиторием, и отдает какой-то результат согласно контрактам. Вызывать ее можно хоть от куда угодно.
Схему рисовать не буду, лень)
Возможно, глупый вопрос.
Предположим, сервис меняет данные в БД.
Есть два варианта:
1. Сервис принимает модель объекта (которую из get_object() во view получили) и ее меняет. Попутно меняются данные в связанных записях. Ненадежно даже в рамках транзакции, т.к. при приеме двух идентичных запросов есть риск дублирования.
2. Сервис принимает id объекта, делает select_for_update() для надежности. Таким образом избегаем ошибок с дублями запросов. Но это доп. запрос уже после того, как get_object() объект вернул. Проще говоря, лучше бы просто id объекта сразу получить во view, но там могут быть проверки permissions, которые на конкретный объект завязаны.

Какой из подходов можно считать общепринятым в django/drf?
открою вам маленький секрет: нету ни каких общепринятых практик. есть дока по джанге и дока дрф, а дальше кто во что горазд. только набив приличную кучу шишек и снаварив макарон на армию порождаются практики внутри компании / команды. и если вас озадачивает такой вопрос: делайте как считаете правильным и подходящим здесь и сейчас, потом придет более лучшее решение, а потом ещё одно и ещё одно. всегда будут компромиссы
UFO just landed and posted this here
А статью написать можете? Для неопытных
Согласен с вами. Как раз такая схема описывается в последнем подходе в статье, только без репозитория.

Репозиторий отличный паттерн, но у него есть одна особенность, что бы он был настоящим и приносил плюсы, нам нужно использовать отдельные модели для сущностей, а не Django модели. Такой подход наиболее чистый и приносит массу плюсов, особенно, в приложениях, где много бизнес-логики и сложные бизнес-сущности, которые могут не укладываться в одну таблицу БД. Что-то подобное можно посмотреть тут github.com/dry-python/bookshelf

Но можно пойти на компромисс, если ваши бизнес-сущности не так сложны и всегда будут просто дублировать Django модели. Можно скрывать детали реализации доступа к данным за сервисным слоем или менеджером модели.

Придерживаться такому подходу:
  • использовать Django модели
  • в QuerySet описывать часто повторяющиеся фильтры, которые нужно часто комбинировать с другими условиями, например active() = filter(is_active=True)
  • использовать (писать) методы в Manager и вызывать их из сервисов или использовать только сервисный слой, например как у github.com/HackSoftware/Django-Styleguide сервисы и селекторы, для описания логики работы с данными
Вот про паттерн «Репозиторий» — правильная тема. Но у нас на Django-проекте репозитории не прижились, как раз из-за «толстых» моделей (вижу, Вы уже написали про это комментарий ниже).
Django ORM построен по принципу Active Record. Действия над базой могут производиться явно или еще хуже — неявно — в ответ на вызовы методов объектов-моделей.
Для правильного репозитория нужен Data Mapper, чтобы объекты, представляющие собой строки в БД или сущности бизнес логики, не содержали в себе методов изменения БД. Чтобы править базу можно было только через репозиторий, который бы обеспечивал соблюдение всех правил бизнес логики.

Как было у нас:
Чтобы избавиться от логики, размазанной по разным углам приложения, попытались писать репозитории, комбинирующие в себе методы, касающиеся отдельных смысловых аспектов работы системы.
И вот метод репозитория производит какие-то действия и должен вернуть нечто.
Половина системы заточена на работу с query_set, либо с моделями. Слой view ожидает Django-модели. Сериализаторы тоже ожидают кверисет или отдельные модели. Есть сигналы Django, обработчики методов save() моделей.
Вроде получается, из репозитория надо бы тоже вернуть кверисет или модель, чтобы дальше она была пригодна для обработки остальными компонентами Django-проекта.
Но они содержат методы, позволяющие модифицировать базу в обход логики репозитория. И эти методы используются.
Дальше либо делать, чтобы репозиторий возвращал какие-то DTO, но тогда теряем половину преимуществ (batteries included) Django по работе с моделями и кверисетами.
Либо договариваемся, что репозитории лишь группируют логику на смысловом уровне, но не изолируют ее полностью, и мы просто сами не будем обращаться к их методам, изменяющим состояние БД.
Договорились. Кто-то забыл, кто-то пропустил. Где-то стало не удобно. Стало казаться, что Django сопротивляется такому подходу. В коде пошла та же лапша но с репозиториями.

На новом проекте хотим попробовать отказаться от Django REST Framework и ее «как бы-сериализаторов» и использовать Django Ninja для АПИ (клон FastAPI под Django), и таким образом уменьшить количество неявных/спрятанных в абстракциях операций над кверисетами и моделями. Возможно получится более сильно изолировать бизнес-логику в сервисах, а в методах АПИ непосредственно вызывать действия сервисов/репозиториев в коде и возвращать DTO объекты, которые оно уже сериализует в JSON 1-в-1.
Спасибо за такой развернутый комментарий, думаю, это распространенная проблема, когда не используются отдельные Entity классы.

Мы в своем проекте, что бы отказаться от соблазна использовать методы queryset во всех слоях кроме сервисного, решили возвращать из сервисов и селекторов обычные Python коллекции, например листы. Мы основываемся на github.com/HackSoftware/Django-Styleguide с некоторыми изменениями и используем только обычные классы Serializer и APIView, также стараемся не использовать Relation филды, которые требуют передачи в них queryset. Валидация и поиск сущности в БД происходит в сервисах. Не всем данный подход подходит, так как приходится отказываться от некоторых удобств DRF. Также от использования методов модели это не защищает, и работают только договоренности.
есть ли у вас какой-то пример реализации паттерна «Репозиторий», что бы посмотреть реализацию на питоне, заранее спасибо.
Лучше всего знакомиться с паттерном вне контекста языка или фреймворка. Например, паттерн репозиторий очень хорошо описан в книге Реализация методов предметно-ориентированного проектирования хоть она и про DDD, но там затрагивается тема архитектуры и паттернов.
Можно посмотреть на github.com/cosmicpython/code/tree/appendix_django, github.com/dry-python/bookshelf или www.cosmicpython.com/book/chapter_02_repository.html
напишу ответ под свой комментарий в виде некоторой выдержки из комментариев и личного опыта:
Джанго + дрф = прекрасный инструмент для разработки crud api без сложной бизнес логики. всякие там интернет-магазины, бложеки и тп. ровно эту же задачу прекрасно решает и паттерн репозиторий, но в таком приложении он нафиг не нужен.
когда речь заходит про проект средней сложности по бизнес логике, тут можно и нужно комбинировать разные подходы и практики чтобы и не затянуть сроки выпуска фич, и не погрязнуть в макарошках. уже тут всплывают всякие нюансы и компромиссы.
а вот когда у вас сложный проект, толстые модели, вся логика держится только на SQL тут джанга и дрф — практически антипаттерн. и чтобы вы не пытались привнести в проект все равно получится не очень. и компромиссов будет ещё больше.
это не означает что джанго — это зло, просто оно о другом, и создавалась как раз под потоковую разработку бложеков, а не проекты на миллион строк со сложными отношениями сущностей.
и всегда есть не нулевая вероятность того, что вы выбрали не правильный инструмент для решения конкретной задачи.
Sign up to leave a comment.