MissingGreenlet при чтении relationship в AsyncSession
Асинхронный обработчик успешно выполнил SELECT и передал объект заказа в сериализатор. Ошибка MissingGreenlet появилась уже при чтении order.items. Кажется, что запрос завершился, сессия ещё открыта и данные должны быть доступны. Но коллекция items в первом запросе не загружалась.
Обычное обращение к атрибуту не содержит await. Если relationship требует lazy load, ORM пытается выполнить ввод-вывод там, где асинхронный вызов не был явно организован. Для AsyncSession путь загрузки связанных объектов нужно определить до передачи объекта синхронному коду сериализации.
Как изменить решение
from sqlalchemy import select
from sqlalchemy.orm import selectinload
stmt = (
select(Order)
.where(Order.id == order_id)
.options(selectinload(Order.items))
)
order = (await session.scalars(stmt)).one()
response = {
'id': order.id,
'items': [{'id': item.id} for item in order.items],
}
selectinload выполняет загрузку коллекции в рамках ожидаемой асинхронной операции. Затем построение response читает уже полученные данные. Order и его relationship здесь должны быть определены в приложении; существенное изменение — явная стратегия загрузки в запросе обработчика.
Если объект наследует AsyncAttrs, отдельный путь — await order.awaitable_attrs.items. Он удобен, когда решение о загрузке принимается позже, но await должен оставаться в асинхронном слое. Передавать awaitable в обычный сериализатор вместо списка нельзя: данные надо получить до формирования ответа.
Что проверить на своих данных
В тесте создайте заказ с двумя позициями, прочитайте его новым AsyncSession и сформируйте ответ. Новая сессия важна: ранее загруженная коллекция в identity map способна скрыть дефект. Проверьте также пустую коллекцию. Включённый SQL-лог должен показать загрузку до сериализации, а не попытку запроса во время обхода атрибутов.
expire_on_commit=False решает другую ситуацию — истечение уже загруженных атрибутов после commit. Оно не загружает relationship, которого никогда не было в памяти. Аналогично продление жизни сессии само по себе не добавляет await к обращению order.items. На границе HTTP удобно собирать простой DTO, пока все нужные чтения явно выполнены. Тогда сериализатор не зависит от скрытого состояния ORM и не начинает обращаться к базе при добавлении нового поля ответа.
Практика дальше
От загрузки коллекции стоит перейти к проверке всего HTTP-ответа. В курсе FastAPI вы собираете API с SQLAlchemy и тестируете его через pytest, TestClient и dependency_overrides. Последовательный backend-проект использует SQLite и помогает связать модели с контрактом обработчика.
Посмотреть программу и практику курса
Источник: MissingGreenlet при чтении relationship в AsyncSession — официальная документация.
Другие задачи по теме: База, HTTP-клиенты и тесты API.
Продолжить на практике
- FastAPI для начинающих: API с базой данных и тестами ↗
Соберёте свой первый полноценный backend на FastAPI: с базой данных, регистрацией, JWT-авторизацией и автотестами. 34 коротких урока и 140 заданий с проверкой — бесплатно.
