Python 如何在现有对象上显式加载关系?
我有一个SQLAlchemy模型Python 如何在现有对象上显式加载关系?,python,sqlalchemy,Python,Sqlalchemy,我有一个SQLAlchemy模型Foo,它包含一个延迟加载的关系bar,它指向另一个模型,该模型也有一个延迟加载的关系foobar 正常查询时,我将使用此代码确保使用单个查询加载所有对象: session.query(Foo).options(joinedload('bar').joinedload('foobar')) 但是,现在我遇到了一个例子,其中一个基类已经为我提供了一个Foo实例,该实例是使用session.query(Foo.one())检索的,因此关系是延迟加载的(这是默认的,我
Foo
,它包含一个延迟加载的关系bar
,它指向另一个模型,该模型也有一个延迟加载的关系foobar
正常查询时,我将使用此代码确保使用单个查询加载所有对象:
session.query(Foo).options(joinedload('bar').joinedload('foobar'))
但是,现在我遇到了一个例子,其中一个基类已经为我提供了一个Foo
实例,该实例是使用session.query(Foo.one()
)检索的,因此关系是延迟加载的(这是默认的,我不想更改)
对于单级嵌套,我不介意在访问foo.bar
后加载它,但因为我还需要访问foo.bar[x].foobar
,所以我确实希望避免在循环中发送查询(每当访问foobar
时都会发生这种情况)
我正在寻找一种方法,使SQLAlchemy加载
foo.bar
关系,同时使用foobar
的joinedload策略,SQLAlchemy wiki包含该配方。对父集合发出查询,然后查询并组合子集合。在大多数情况下,这是在SQLAlchemy中作为子查询
策略实现的,但配方涵盖了您以后明确需要进行查询的情况,而不仅仅是单独进行查询
其思想是对子查询进行排序,并通过链接关系的远程列对结果进行分组,然后使用子查询组填充每个父项的属性。下面是对配方的轻微修改,以允许传入带有额外选项的自定义子查询,而不是从父查询构建它。这确实意味着您必须更仔细地构造子查询:如果父查询具有筛选器,那么子查询也应该加入并进行筛选,以防止加载不需要的行
from itertools import groupby
from sqlalchemy.orm import attributes
def disjoint_load(parents, rel, q):
local_cols, remote_cols = zip(*rel.prop.local_remote_pairs)
q = q.join(rel).order_by(*remote_cols)
if attr.prop.order_by:
q = q.order_by(*rel.prop.order_by)
collections = dict((k, list(v)) for k, v in groupby(q, lambda x: tuple([getattr(x, c.key) for c in remote_cols])))
for p in parents:
attributes.set_committed_value(
p, attr.key,
collections.get(tuple([getattr(p, c.key) for c in local_cols]), ()))
return parents
# load the parents
devices = session.query(Device).filter(Device.active).all()
# build the child query with extras, use the same filter
findings = session.query(Finding
).join(Device.findings
).filter(Device.active
).options(db.joinedload(Finding.scans))
for d in disjoint_load(devices, Device.findings, findings):
print(d.cn, len(d.findings))
我最近遇到了类似的情况,并最终做了以下工作:
eager_loaded = db.session.query(Bar).options(joinedload('foobar'))
.filter_by(bar_fk=foo.foo_pk).all()
假设您可以在filter\u by
参数中重新创建bar
连接条件,则集合中的所有对象都将加载到标识映射中,foo.bar[x].foobar
将不需要转到数据库
一个警告:如果加载的实体不再被强引用,则标识映射可能会处理这些实体-因此分配给eager\u loaded