SQLAlchemy,急于加载对象刷新

时间:2013-06-03 23:14:41

标签: 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对象。我经常从会话中分离Foo对象并继续使用其值和Bar的值。我不时需要更新Foo的状态字段。在这种情况下,我创建一个新会话,将foo对象添加到会话并提交它。提交之后,与Foo对象关联的Bar对象无效,但不会被提交的Foo对象的隐式刷新重新加载。从会话中再次分离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,无论是查询(),commit()(隐式刷新)还是(显式)刷新()?

1 个答案:

答案 0 :(得分:3)

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

接下来,session.refresh(foo)将使用配置的eager loader加载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

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