SQLAlchemy中的scoped_session问题 - 它是如何工作的?

6 投票
2 回答
4141 浏览
提问于 2025-04-15 13:58

我不太明白scoped_session是怎么工作的,感觉它像是一个包装器,可以隐藏多个真实的会话,让它们在不同的请求中保持独立。它是通过线程本地存储来实现的吗?

总之,问题是这样的:

S = elixir.session # = scoped_session(...)
f = Foo(bar=1)
S.add(f) # ERROR, f is already attached to session (different session)

我不明白为什么f会出现在一个不同的会话里,我之前没有遇到过这个问题。在其他地方,我有一段代码看起来和这个一模一样,但实际上是能正常工作的。你可以想象,这让我感到非常困惑。

我对这里的一切都一无所知,f似乎在构造函数中神奇地被添加到了一个会话里,但我似乎没有任何关于它使用的会话的引用。为什么它会出现在一个不同的会话中?我该如何让它出现在正确的会话里?这个scoped_session到底是怎么回事?有时候它似乎能正常工作,有时候又不行。

我真的很困惑。

2 个回答

2

结果发现,elixir在创建映射器时会自动设置save-on-init=True。我们可以通过以下方式来关闭这个功能:

using_mapper_options(save_on_init=False)

这样就解决了问题。感谢stepz在#sqlalchemy频道迅速找出了原因。虽然我还是对scoped_session的具体工作原理很好奇,如果有人能解答这个问题,他们就会得到认可。

7

Scoped session(作用域会话)创建了一个代理对象,它会保持一个注册表,默认情况下是每个线程的会话对象,这些对象是根据传入的会话工厂按需创建的。当你使用像 ScopedSession.add 这样的会话方法时,它会找到当前线程对应的会话,并返回与该会话绑定的 add 方法。你可以通过 ScopedSession.remove() 方法来移除当前活跃的会话。

ScopedSession 有一些方便的方法,其中一个是 query_property,它创建了一个属性,这个属性返回一个与创建它的作用域会话和访问它的类绑定的查询对象。另一个是 ScopedSession.mapper,它添加了一个默认的 __init__(**kwargs) 构造函数,并默认将创建的对象添加到创建该映射器的作用域会话中。这个行为可以通过映射器的 save_on_init 关键字参数来控制。由于问题中提到的原因,ScopedSession.mapper 已经被弃用了。这是一个很好的例子,说明了 Python 的“显式优于隐式”这一理念的适用性。不幸的是,Elixir 默认仍然使用 ScopedSession.mapper

撰写回答