Код-ревью Senior SQLAlchemy 2.0 обновлено
Код-ревью async-сервиса на SQLAlchemy
Вопрос
Найдите проблемы в функции, которая собирает сводку по пользователям.
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from .models import User
async def users_summary(session: AsyncSession):
result = await session.execute(select(User))
users = result.scalars().all()
out = []
for u in users:
orders = u.orders
total = sum(o.amount for o in orders)
out.append({'email': u.email, 'total': total})
u.last_report_total = total
await session.commit()
return out
Коротко
Агрегацию лучше перенести в базу: Если сохранять итог в lastreporttotal действительно нужно, это делается одним update с подзапросом или пачкой изменений и одним коммитом.
Что должен найти ревьюер
- Производительностьстрока 7Загружаются все пользователи целиком без фильтра и пагинации, со всеми колонками
- Ошибкастрока 11Ленивая загрузка
u.ordersв async падает сMissingGreenlet; нуженselectinload(User.orders) - Производительностьстрока 12Сумма считается в Python перебором заказов; агрегировать в базе через
func.sumиgroup_by - Производительностьстрока 15Коммит на каждого пользователя: много транзакций, а после коммита атрибуты истекают (
expire_on_commit) и следующее обращение снова пойдёт в базу; один коммит в конце или массовыйupdate
Разбор
Агрегацию лучше перенести в базу:
async def users_summary(session: AsyncSession, limit: int = 100, offset: int = 0):
stmt = (
select(User.id, User.email, func.coalesce(func.sum(Order.amount), 0).label('total'))
.outerjoin(Order, Order.user_id == User.id)
.group_by(User.id, User.email)
.order_by(User.id)
.limit(limit).offset(offset)
)
rows = (await session.execute(stmt)).all()
return [{'email': r.email, 'total': r.total} for r in rows]
Если сохранять итог в last_report_total действительно нужно, это делается одним update с подзапросом или пачкой изменений и одним коммитом.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.