Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python SQLAlchemy,在对象刷新时立即加载_Python_Orm_Sqlalchemy_Eager Loading - Fatal编程技术网

Python SQLAlchemy,在对象刷新时立即加载

Python SQLAlchemy,在对象刷新时立即加载,python,orm,sqlalchemy,eager-loading,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, p

如果在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的status字段。在这种情况下,我创建了一个新会话,将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
session=session()
foo=session.query(foo.get(id)首先,“commit()”不是“刷新”-它实际上会使所有数据过期,因此您会看到所有映射的属性不再出现在
foo中。再次触摸这些属性时,会发生隐式刷新。对于许多在提交后不需要跨事务同步的应用程序,在
会话中简单地设置
expire\u on\u 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不鼓励使用处于“分离”状态的对象,因为映射对象表示正在进行的数据库事务的代理。这就是为什么当事务结束时,所有数据都会过期。就我个人而言,我认为通常没有正当的理由认为对象需要在分离状态下使用;分离主要是为了将对象传输到其他会话,将它们存储在缓存中,诸如此类。但是我们确实有很多用户在任何情况下都依赖于分离的使用模式,我已经确保我可以在合理的程度上支持它们,所以我不会太担心它。

在我的特定设置中,
s.commit()
确实对foo对象进行了选择,而不使用bar连接它(在我自己接触任何属性之前)。我不知道为什么。正如你所指出的,我错误地认为这是一次暗示的刷新。我不能用你的小演示预先制作这个。即时加载确实与显式的
s.refresh(foo)
一起工作。不知何故,我错过了这一点,我仍然在学习/实验SQLAlchemy。你说,SQLAlchemy不鼓励使用处于“分离”状态的对象。我的Foo对象存在于多个HTTP请求之上,我总是在请求结束时关闭orm会话。我只需要更新foo对象1/100请求,其他99个请求我只需要使用数据。那么你会推荐什么设计呢?1) 将Foo和Bar的数据复制到非orm对象,如果需要更新,请重新查询。2) 打开一个新的orm会话,即使我不需要更新/刷新Foo。3) …通常web应用程序会在每次请求时加载所需的所有数据。如果你在第一个请求上加载了Foo对象,那么第二个请求在六个小时后到达,那么你在整整六个小时内是否一直在内存中存储这些“Foo”对象?你怎么知道它们仍然是最新的?这实际上是一种缓存模式。SQLAlchemy对缓存的建议是在每次请求时将对象重新附加或合并到一个新会话-请参阅以获取建议的设计指南。仅供参考:我正在设计和实现一个使用XMLRPC over HTTP的分布式系统。我正在实现这两个方面,所以我知道我将定期接收请求,记录Foo对象的生存方式,并且在我将它们保存在内存中时它们不会过时。我的问题当然偏向于这种设计。