SQLAlchemy:使用 sessionmaker 作为上下文管理器强制手动删除

SQLAlchemy: Using sessionmaker as contextmanger forces to expunge manually

我正在使用 SQLAlchemy 并尝试将 sessionmaker 用作我的事务的上下文管理器(在 documentation 之后):

Session = sessionmaker(some_engine)
with Session.begin() as session:
    query = session.query(SomeTable)
    an_entry = query.one()
    # session.expunge_all()  # Fixes the exception
an_entry.attribute

这会引发 sqlalchemy.orm.exc.DetachedInstanceError: Instance <AnEntry at 0x7f0a9d1c2560> is not bound to a Session 异常。这可以通过删除上下文管理器中的内容来解决(请参阅上面注释掉的行)。

我对这种行为感到惊讶,因为 SQLAlchemy documentation 和代码表明关闭会话会自动清除所有 ORM 对象。事实上,以下等效于上述工作,而无需我手动删除:

Session = sessionmaker(some_engine)
session = Session()
with session.begin():
    query = session.query(SomeTable)
    an_entry = query.one()
an_entry.attribute

谁能解释这种行为?

在第二个“工作”示例中,上下文管理器是 session.begin 返回的 SessionTransaction 对象,而不是会话本身。在访问属性之前会话未关闭,因此没有 DetachedInstanceError:实例永远不会被删除。

更好的比较可能是此代码不引发错误的原因:

with Session() as s:
    an_entry = s.query(SomeTable).one()
an_entry.attribute

原因是它没有提交。默认情况下,ORM 对象在提交时过期,因此后续的属性访问需要查询才能获取值。在 with Session.begin() as session: 的情况下,会话在离开 with 块时提交,因此访问属性需要查询,这需要将对象附加到会话。

Sessionsessionmaker 都接受 expire_on_commit 布尔关键字参数来控制此行为。