SQLAlchemy异步Session提交后访问模型属性报错,是否操作有误?
SQLAlchemy异步模式属性访问报错问题解决
先揪出最明显的错误
你调用异步方法repo.model()的时候没加await!这会导致model拿到的根本不是TestUser实例,而是个协程对象,直接访问model.name不出错才怪。正确调用得这么写:
model = await repo.model() print(model.name)
为啥加了await还是可能报错?
SQLAlchemy的异步模型默认用了AsyncAttrs,不管是关联属性还是自有列,访问的时候都可能触发异步IO。你提交session后,如果当前的asyncio task结束了(因为你用的是async_scoped_session,scopefunc是asyncio.current_task,每个task对应一个session),session会被自动关掉,模型就脱离了session上下文。这时候再访问属性,SQLAlchemy想从数据库重新加载数据,但找不到异步运行环境,就会抛出MissingGreenlet错误。
怎么解决?
方法1:在session活跃的时候把属性读出来
要么在model方法里就把属性访问完,要么确保访问属性时session还没关:
async def model(self): model = TestUser(name='test', age=1) self.session.add(model) await self.session.commit() # 这里session还在当前task的scope里,访问属性没问题 print(model.name) return model
方法2:提前把属性加载到内存
提交前先flush一下,让模型的属性直接加载到内存里,之后就算session关了也能直接访问:
async def model(self): model = TestUser(name='test', age=1) self.session.add(model) # flush会把数据写入数据库,同时把属性加载到内存 await self.session.flush() await self.session.commit() return model
方法3:应急用awaitable_attrs(不推荐)
如果非得在session外访问,就用await model.awaitable_attrs.name,但这会额外发一次数据库查询,能不用就不用。
关于你用的async_scoped_session
你用asyncio.current_task作为scopefunc,意味着每个异步任务对应一个独立session。任务结束时session自动关闭,这时候模型就成了"脱管"状态,属性访问就容易出问题。所以跨任务用模型的话,一定要确保属性已经提前加载好。
内容的提问来源于stack exchange,提问作者Nasa
相关产品推荐
相关产品推荐

