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

Thi*_*ter 6 python sqlalchemy

我有一个SQLAlchemy模型Foo,它包含一个延迟加载的关系bar,该关系指向另一个也具有延迟加载关系的模型foobar.

在正常查询时,我会使用此代码来确保所有对象都使用单个查询加载:

session.query(Foo).options(joinedload('bar').joinedload('foobar'))
Run Code Online (Sandbox Code Playgroud)

但是,现在我有一种情况,基类已经为我提供了一个Foo使用检索的实例session.query(Foo).one(),因此关系是延迟加载的(这是默认的,我不想改变它).

对于单级嵌套,我不介意它一旦被访问就被加载foo.bar,但由于我还需要访问,foo.bar[x].foobar我真的更愿意避免在循环中发送查询(这将在我访问时发生foobar).

我正在寻找一种方法来使SQLAlchemy加载foo.bar关系,同时也使用joinload策略foobar.

dav*_*ism 2

SQLAlchemy wiki 包含不相交预加载配方。对父集合发出查询,然后查询并组合子集合。在大多数情况下,这是在 SQLAlchemy 中作为subquery策略实现的,但该配方涵盖了您明确需要稍后进行查询的情况,而不仅仅是单独进行查询。

这个想法是,您对子查询进行排序,并按链接关系的远程列对结果进行分组,然后使用子项组填充每个父项的属性。以下内容对配方进行了稍微修改,以允许传入带有额外选项的自定义子查询,而不是从父查询构建它。这确实意味着您必须更仔细地构建子查询:如果您的父查询有过滤器,那么子查询也应该加入和过滤,以防止加载不需要的行。

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))
Run Code Online (Sandbox Code Playgroud)