python - SQLAlchemy,预先加载对象刷新

标签 python orm sqlalchemy eager-loading

如果在 SQLAlchemy 中有以下 ORM 设置:

class Foo(Base):
    id = Column(Integer, primary_key=True)
    status = Column(String)
    barId = Column(Integer, ForeignKey("bar.id"))
    bar = relationship("Bar", lazy="joined")

class Bar(Base):
   id = Column(Integer, primary_key=True)

所以我希望始终为每个 Foo 对象提供关联的 Bar 对象。我经常从 session 中分离 Foo 对象并继续使用它的值和 Bar 的值。有时我需要更新 Foo 的状态字段。在那种情况下,我创建一个新 session ,将 foo 对象添加到 session 中并提交它。提交后,与 Foo 对象关联的 Bar 对象无效,但不会通过提交对 Foo 对象的隐式刷新重新加载。再次从 session 中分离 Foo 对象后,Bar 对象不再可用。我发现解决这个问题的唯一方法是在提交 foo 之后显式地急切加载 bar 对象。

示例工作流程:

session = Session()
foo = session.query(Foo).get(id) <-- foo.bar is automatically eager loaded
session.close()
....
session = Session()
session.add(foo)
foo.status = 'done'
session.commit()       <-- foo is commited and refreshed, foo.bar is not
session.refresh(foo)   <-- same here, foo.bar is not loaded
#foo.bar               <-- only explicit eager loading foo.bar here works
session.close()
....
foo.bar                <-- error if not explicitly eager loaded

我想将此设置用于一些类似 Bar 的小对象。要求我记住始终明确地重新加载 foo.bar 对象很容易出错。所以我的问题是:我能否在所有情况下都急切加载 foo.bar,无论是 query()、commit()(隐式刷新)还是(显式)refresh()?

最佳答案

首先,“commit()”不是“刷新”——它实际上会使所有数据过期,因此您会看到所有映射的属性都不再存在于 foo.__dict__ 中。当您再次触摸这些属性时,就会发生隐式刷新。对于提交后不需要跨事务同步的许多应用程序,在 Session 中简单地设置 expire_on_commit=False 是一种非常常见的做法,因此可能是最含蓄的工作流程。

接下来,session.refresh(foo) 将使用配置的预加载器加载 bar。不确定为什么你看到 foo.bar 未加载,我检查了一下,这个功能至少可以追溯到版本 0.5。一个简单的测试证实了这一点:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    status = Column(String)
    barId = Column(Integer, ForeignKey("bar.id"))
    bar = relationship("Bar", lazy="joined")

class Bar(Base):
    __tablename__ = 'bar'
    id = Column(Integer, primary_key=True)

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

s.add(Foo(id=1, bar=Bar()))
s.commit()

f1 = s.query(Foo).get(1)
f1.status = 'done'
s.commit()

assert 'bar' not in f1.__dict__
s.refresh(f1)
assert 'bar' in f1.__dict__
s.close()

assert f1.bar.id == 1

接下来,SQLAlchemy 不鼓励使用处于“分离”状态的对象,其一般原因是您的映射对象代表正在进行的数据库事务的代理。这就是为什么当事务结束时,所有数据都已过期。就个人而言,我认为通常没有正当理由需要在分离状态下使用对象;分离主要是为了将对象传输到其他 session ,将它们存储在缓存中,诸如此类。但是我们确实有很多用户在任何情况下都依赖于分离的使用模式,我已经确定我可以在合理的程度上支持他们,所以我不会太担心它。

关于python - SQLAlchemy,预先加载对象刷新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16907337/

相关文章:

python - for 循环不会迭代函数中的字符串,但不会迭代函数 - python 3

python - 如何为此中间件文件 django 编写正确的单元测试用例?

flask - 如何为 Alembic 迁移的每一行设置唯一值

python - 如何获取 StringIO 容器中的图像字节

postgresql - 直接 hibernate native 查询工作正常,但使用 setParameter 查询未更新 postgresql 数据库

entity-framework - 有人使用代码优先方法与 Edmx 文件混合的 Entity Framework 吗?

php - 将 Zend_Db 与 ORM 类混合

python - "Injecting"从外部数据库到 SQLAlchemy 对象的字典数据

python - SQLAlchemy 急切加载特定子类型集合

python - Django休息框架: sendgrid email with attachment without model only filefield to open file and send button to send email