分离SQLAlchemy实例以避免刷新

14 投票
4 回答
20109 浏览
提问于 2025-04-17 19:00

我想把一个类的实例从我的会话中分离出来,但仍然希望它可以被读取(不需要再发起查询)。我已经翻阅文档好几天了,但我尝试的每种方法都让我看到这个提示

DetachedInstanceError: Instance <MyModel at 0x36bb190> is not bound to a Session;
attribute refresh operation cannot proceed

我在Pyramid中使用zope.sqlalchemy的事务管理器。我希望我的对象在事务提交后仍然可以使用。我只需要读取“缓存”的值,也就是在事务提交之前的那些值。

我找到的唯一可能的解决办法是把这个类(或者它的属性)包裹起来,然后手动跟踪变化(我可以这样做,但这真的很麻烦,而且一点也不符合Python的风格)。

有没有办法可以阻止SQLAlchemy尝试刷新这些值呢?

作为备选方案,我甚至可以接受返回None,只要在事务提交后不再出现上面的错误提示就行。

4 个回答

4

试试这个:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), expire_on_commit=False))

我也遇到过这个问题,使用 expire_on_commit=False 解决了我的问题。

13

你可以通过以下方式实现这个功能(比如用于缓存):

session.expunge(obj)

根据sqlalchemy的文档:

http://docs.sqlalchemy.org/en/rel_1_0/orm/session_api.html?highlight=expire#sqlalchemy.orm.session.Session.expunge

这样你就能得到一个处于“分离状态”的对象,这个对象可以安全使用。你需要记住的是,不能读取那些会触发新查询的属性,这样会和会话绑定在一起,比如关系属性,这样会导致出现DetachedInstanceError错误。

默认情况下,提交操作会执行一个expire_all(),这意味着所有对象在读取时都会刷新它们的状态。通过将对象分离,你把它们从会话中移除,这样在提交事务后就不会有后续的查询了。

我建议不要像其他评论所说的那样全局禁用这个功能,因为Mike Bayer通常认为这对大多数人来说是个好主意,也是一个合理的默认设置,能在长远中避免麻烦。

只在需要的时候明确地分离对象。

14

我觉得你在找的是 expire_on_commit = False

我相信这个设置可以让你把对象分离出来,然后继续使用它。不过,如果你尝试去修改这个对象并提交的话,就会出现 DetachedInstanceError 的错误。

撰写回答