Comments 6
Также алхимию можно применять в любой проекте, в то время как ORM от Django недоступен в отрыве от основного фреймворка.
Это не правда https://github.com/dancaron/Django-ORM
Не говоря уже о том, что Django это делает гораздо элегантнее и с встроенной поддержкой миграций.
Спасибо за наводку. Я натыкался на сторонние библиотеки, но использовать их в prod-е я бы не стал. Жду поддержки отдельно ORM именно от Django-проекта.
Видимо, придётся написать статью-ответ. Но то, что называется в данной статье "оптимизацией" таковой не является. По крайней мере, не всегда. А для конкретных листингов оптимизацией было бы что-то вроде (что-то вокруг #sa.9.1, #sa.9.2):
query = sa\
.select(Topic.id.label('id'), sa.func.array_agg(User.name).label('users'))\
.where(sa.and_(
Topic.id == TopicUser.topic_id,
TopicUser.user_id == User.id
))\
.group_by(sa.literal_column('1'))
with session as s:
for row in s.execute(query):
print(f'Topic {row.id}, Users: {", ".join(row.users)}')
Не надо запрашивать то, что не требуется. Если требуется получить только определенные колонки, не надо просить ORM вытащить модель целиком. Гонять новый запрос на каждую строчку связанной модели - моветон. Если средства СУБД позволяют объединить (или схлопнуть несколько строк в одну без потери данных - позвольте это сделать СУБД. Она, во-первых, скорее всего, сделает это эффективнее, во-вторых, будет меньше накладных расходов на запрос и передачу result tuples.
Более того, мой запрос прочитает, скорее всего, даже далёкий от Python DBA. А запрос с мутаторами для relationship mapping - не всякий, там уже надо ковырять детали и тонкости конкретного движка (в данном случае - SA).
query = sa\
.select(Topic.id.label('id'), sa.func.array_agg(User.name).label('users'))\
.where(sa.and_(
Topic.id == TopicUser.topic_id,
TopicUser.user_id == User.id
))\
.group_by(sa.literal_column('1'))
with session as s:
for row in s.execute(query):
print(f'Topic {row.id}, Users: {", ".join(row.users)}')
А как, простите, дебажить такие запросы? А когда у тебя SELECT более чем на 10 таблиц?
ORM не подходит для крупных проектов и больших команд. С этим сложнее работать.
А как все другие запросы дебажить предполагается?
SELECT
topics.id AS id,
array_agg(users.name) AS users
FROM
topics,
topics_users,
users
WHERE
topics.id = topics_users.topic_id
AND
topics_users.user_id = users.id
GROUP BY 1
Вот же этот запрос. Он в SQLAlchemy выглядит один-в-один, как в SQL (в отличие от другой популярной но, на мой взгляд, кривущей реализации ORM в Python).
Как его дебажить?
Более того, в моём случае запрос сначала пишется в DataGrip (sql server client console), отлаживается, смотрится план выполнения, затем переносится в SQLAlchemy. Тривиальные запросы, конечно, можно и без отладки в консоли написать.
SQL в SQLAlchemy