我正在阅读SQLAlchemy ORM教程(https://docs.sqlalchemy.org/en/latest/orm/tutorial.html),发现很难理解何时/为什么Python对象将反映数据库中的最新数据。
以下是一系列使我感到困惑的事件:
ed_user
并将其添加到会话中。其id
为None
,因为该行尚未写入数据库。our_user
,该用户是通过使用与ed_user
相匹配的查询查询数据库而获得的。因此our_user
和ed_user
实际上是同一用户。当我们在查询发生后查询our_user.id
或ed_user.id
时,我们发现id
已被分配,因为当SELECT查询时ed_user
已刷新到数据库中是写的。ed_user
并添加其他一些不相关的行,并发出会话提交。ed_user.id
的值,这导致数据库发出SELECT查询以获取ID的最新值,因为上一次提交结束了先前的事务。我觉得这非常令人困惑,因为在第一步中,在将ed_user
写入数据库之前,SQLAlchemy满意地为我们提供了None
的{{1}}值尽管如果继续进行并刷新写入数据库的操作,它可能已经获得了一个ID ,但是由于某种原因,一旦将行写入数据库后,SQLAlchemy认为保持最新状态很重要(在最后一步中),方法是在读取数据时刷新数据。为什么会发生这种情况以及控制这种行为的原因?
最重要的是,我不知道在何时/为什么/如何使Python对象与数据库保持最新状态时,我可以依靠什么逻辑,您所能提供的任何额外的清晰度都将受到高度赞赏。 / p>
答案 0 :(得分:1)
我将尝试通过您的要点来阐明SQLAlchemy中的state management。
- 首先,我们创建一个用户
ed_user
并将其添加到会话中。其id
为None
,因为该行尚未写入数据库。
在将新创建的Ed对象添加到会话之前,它处于 transient 状态;它尚未添加到会话中,并且没有数据库标识。当您将其添加到会话中时,它将进入待处理状态。它尚未flushed到数据库,但是将在下次刷新时出现。如果您启用了autoflush(默认设置),则会在发出下一个查询操作之前清除所有待处理的更改,以确保查询时会话和数据库的状态是同步的,这使我们能够:
- 然后,我们创建另一个用户
our_user
,该用户是通过使用与ed_user
相匹配的查询查询数据库而获得的。因此our_user
和ed_user
实际上是同一用户。
说您创建our_user
有点误导。相反,您执行查询并将结果绑定到名称our_user
:
>>> our_user = session.query(User).filter_by(name='ed').first()
请务必记住,在此查询发生之前 ,所有未完成的更改都已刷新。这意味着绑定到名称ed_user
的对象中保存的更改将发送到数据库,并且SQLAlchemy获取其数据库标识(id
不再是None
),并将其移动到永久状态并将其添加到identity map。
由于所有操作均发生在查询之前 ,因此您将获得刷新Ed对象时创建的行,并检查该行的身份(使用身份映射)SQLAlchemy注意它实际上代表了会话中持有的现有对象,该对象之前绑定到名称ed_user
。这就是ed_user.id
和our_user.id
都赋予您相同价值的原因-实际上ed_user is our_user
也将是True
;他们是同一个对象。
- 最后,我们再次读取
ed_user.id
的值,由于前一次提交结束了先前的事务,因此它导致数据库发出SELECT
查询以获取最新的id
值。
默认情况下,SQLAlchemy使commit之后的所有数据库加载状态失效,以防止您处理过时的数据。其他一些线程或进程之间可能已经提交了更改。像大多数事情一样,如果确实需要,可以通过将expire_on_commit=False
传递给sessionmaker
或直接传递Session
来控制此行为。