Python 如何在现有对象上显式加载关系?

Python 如何在现有对象上显式加载关系?,python,sqlalchemy,Python,Sqlalchemy,我有一个SQLAlchemy模型Foo,它包含一个延迟加载的关系bar,它指向另一个模型,该模型也有一个延迟加载的关系foobar 正常查询时,我将使用此代码确保使用单个查询加载所有对象: session.query(Foo).options(joinedload('bar').joinedload('foobar')) 但是,现在我遇到了一个例子,其中一个基类已经为我提供了一个Foo实例,该实例是使用session.query(Foo.one())检索的,因此关系是延迟加载的(这是默认的,我

我有一个SQLAlchemy模型
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