使用lazy=selectin定义SQLAlchemy关联时触发MissingGreenlet错误求助
我定义了AttributeSet类,并在其中设置如下关联:
attribute_set_to_attribute_type: Mapped[ List["AttributeSetToAttributeType"] ] = relationship( lazy="selectin", back_populates="attribute_set", cascade="delete, delete-orphan", )
我通过异步方法从数据库加载AttributeSet实例:
attribute_set = await get_attribute_set( attribute_set_id=attribute_set_id, session=session )
其中get_attribute_set方法实现如下:
async def get_attribute_set( attribute_set_id: uuid.UUID, session: AsyncSession ) -> AttributeSet: return ( await session.scalars( select(AttributeSet).where(AttributeSet.id == attribute_set_id) ) ).first()
但当我尝试访问attribute_set_to_attribute_type集合的元素时:
for attr in attribute_set.attribute_set_to_attribute_type: print(attr.id)
触发了错误:sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place?
我原本以为lazy="selectin"会让关联数据和AttributeSet一起加载,请问这是什么原因?
原因
lazy="selectin"确实支持关联数据预加载,但必须在查询阶段显式声明要加载该关联。当前的查询仅获取了AttributeSet自身的数据,没有主动触发关联加载逻辑,所以即便设置了lazy="selectin",SQLAlchemy也不会自动预加载关联数据。后续访问attribute_set_to_attribute_type时,会尝试发起异步查询,但此时已脱离异步greenlet上下文,因此抛出MissingGreenlet错误。
解决方法
修改get_attribute_set方法,使用selectinload在查询中显式指定预加载目标关联:
from sqlalchemy.orm import selectinload async def get_attribute_set( attribute_set_id: uuid.UUID, session: AsyncSession ) -> AttributeSet: return ( await session.scalars( select(AttributeSet) .where(AttributeSet.id == attribute_set_id) .options(selectinload(AttributeSet.attribute_set_to_attribute_type)) ) ).first()
修改后,查询会一次性加载AttributeSet及其关联的attribute_set_to_attribute_type数据,后续访问该属性时不会触发额外异步查询,即可避免错误。
需要注意:异步SQLAlchemy中,所有延迟加载操作都需要在异步上下文内执行,显式通过options声明预加载是确保关联数据同步加载的可靠方式,能避免后续触发意外的异步IO操作。
内容的提问来源于stack exchange,提问作者Myrto Pirli

