Как стать автором
Обновить

Комментарии 21

Запросов бд в декораторах я ещё не встречал

Экспериментировал с разными вариантами, и этот оказался самым симпатичным

Шёл 2024 год, в питоне пытались реализовать базовые шаблоны проектирования взаимодействия с БД)

А как правильно-то?

А что вы скажите на счет того подхода к взаимодействию с базой данных, что реализовано библиотекой JOOQ, которая позиционирует себя как то, что избавляет от ORM проблем, а так же избавляет от проблем использования простого SQL. Это, конечно, библиотека из мира Java разработки, но все же это та же некоторая альтернатива "Object-Relational Mapping", хоть и в строго типизированном языке, я понимаю, есть другие проблемы и решения.

Мне JOOQ показался поразительно похожим на то, как делаются запросы в SqlAlchemy. Такое же цепочечное нанизывание "родных" конструкций. В сочетании с NoORM работает замечательно. Пользуемся.

По своему опыту ормостроения я понял, что для разных случаев нужны разные уровни доступа к данным. От SQL всё равно не уйти, но это не значит, что ORM не нужен.

задача "Object-Relational Mapping" решения не имеет

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

Невозможно найти единственно правильный баланс между eager- и lazy-загрузкой

Вот меня удивляет, что в гибернейте eager/lazy прибиты гвоздями в описании структуры данных. А EF тем временем позволяет явно подключать джойном данные в рантайме с помощью .Include() для каждого запроса индивидуально.

Спасибо за статью, это мотивирует самого что-то написать. Давно хотел рассказать про свою ORM, где "базы данных не существует".

EF тем временем позволяет явно подключать джойном данные в рантайме с помощью .Include() для каждого запроса индивидуально

Даже в MS SQL, от EF нередко прилетают такие запросы, что ставят оптимизатор в тупик. А уж оптимизатор PostgreSQL явно тупей и там это начинает приобретать катастрофические масштабы. Поэтому, если бизнес-логика требует сложных запросов, то, скрепя сердце, приходится деплоить в БД представления, функции и процедуры. И если в количественном отношении их намного меньше, чем напрямую формируемых EF запросов, то по количеству строк SQL/plpgsql кода у них паритет.

А EF тем временем позволяет явно подключать джойном данные в рантайме с помощью .Include() для каждого запроса индивидуально.

Это тоже порождает массу неудобств и кривизны. Когда мы получаем объект, нам нужно раскинуть карты Таро и предугадать, какие вещи нам нужно включить в .include. И не только для выполнения текущей задачи, но и последующих доработок, о которых у нас пока что нет никаких идей.
Представьте себе, что у нас есть некая функция, принимающая параметр order, экземпляр персистентного ORM-ного класса Order. Можем ли мы без дополнительного похода в базу обратиться к order.lines или к order.customer.email? Чтобы ответить на эти вопросы, нужно проследить все возможные цепочки вызовов, ведущие в эту функцию. Притом не только имеющиеся, но и те, которые, возможно, появятся в будущем.
С простыми и предсказуемыми датаклассами всё намного проще и надёжнее. Если у DbOrder есть свойство customer_email, можем без доп. изысканий его использовать.

Давно хотел рассказать про свою ORM, где "базы данных не существует".

Так расскажите. Интересно.

Угу, а если нужно, например, получить всех пользователей, у которых сумма хотя бы одного из заказов превышает тысячу рублей? Как вы это будете решать с "простыми и предсказуемыми датаклассами"? Вы это либо где то инкапсулируете, делая аналогично спецификациям совместно с orm, либо задача решается вытягиванием половины БД в оперативную память, чтобы в ней сделать то, для чего sql и базы данных в общем то и предназначались.

А что касается include, то в языке давно есть linq, который позволяет повесить этот include ровно там, где надо. И тут опять, же, ничего специально отслеживать не надо, если приложение правильно спроектировано.

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

Не понимаю, в чём проблема с заказами. Можно сделать разными способами. Например, так:

select id, username
from users
where id in (
  select distinct user_id
  from orders
  where amount_rub > 1000
)

Или через join, или через exists.

Ну вообще средствами одной либы можно явно прибить автозагрузку путем пометки поля модификатором virtual. А если прям вот никак не хочется тащить такую магию, можно сделать вычисляемое свойство где уже вручную написать все инклюды, и пользоваться им. Благо Шарп позволяет

Я не питонист, но в PHP есть Doctrine, который умеет исполнять DQL, в котором явно можно сделать eager loading через LEFT JOIN и lazy loading через JOIN. Имхо очень удобно. Кроме того, такой подход позволяет получить объекты model классов (или просто ассоциативные массивы полей требуемой структуры, что почти то же самое) на выходе, написав некое подобие SQL, то есть тут и гибкие манипуляции данными, и агрегатные запросы, и все плюшки.

Слышал много "добрых" слов про сюрпризы, которые иногда преподносит доктринский Hydration.
Очень простое правило вне зависимости от языка программирования, инфраструктуры и прочего: в Проде, особенно нагруженном, этих "объектов model классов" быть не должно. Халтура это. Времянка. Быстренько сляпать – кайф, но потом всё равно придётся всю дорогу тащить этот чемодан без ручки.

Поздравляю, вы изобрели DAO/DTO

Лучше переизобретать хорошие вещи, чем переизобретать плохие.
Вообще, заворачивать результаты запросов в датаклассы – весьма очевидная идея. Весь вопрос, как это делать так, чтобы это было удобно и приятно. Вот для этого как раз и нужна библиотечка.

Посмотрел. Судя по всему, в мире .NET очень полезная и популярная штука. Решает ту же задачу, которая для Питона решена в PEP 249. Тот самый подход, который в начале статьи я обозначил как "Воспользоваться «низкоуровневым» интерфейсом СУБД". В нём всё хорошо за исключением того, что по мере роста функциональности всё превращается в долбаный хаос – вкрапления SQL, разбросанные по всему коду приложения где попало. Основная мотивация NoORM – удобняшка, которая которая не только что-то там упрощает (хотя это тоже важно), но в первую очередь мягко и нежно мотивирует разработчика не плодить хаос.

Отличие даппера от PEP'а в том, что он ещё и мапит (очень эффективно) возвращамые результаты обратно в доменную модель, за что и любИм.

Прошу прощения, плохо знаком с питоном. Но чем это отличается от DataMapper и использования репозиториев?

Если совсем коротко, то это отличается от DataMapper тем, что здесь ничего никуда не мэппится. DbUser не является никаким in-memory representation для строки таблицы базы данных. Это всего лишь завёрнутый в удобную типизированную обёртку конкретный SELECT. Будет какой-нибудь другой select - появится другой датакласс, например, DbAuthenticatedUser. Может показаться, что такой подход плодит много лишних сущностей, но нет, мы просто перестаём смешивать мух с котлетами, и в результате всем становится только лучше.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории