SQLAlchemy - Subqueryload 过滤

5
假设有以下模型,那么如何使用subqueryload加载与订单相关的货物,并通过is_refunded的状态进行筛选呢?
class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)

class Shipment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    shipment = db.Column(MutableDict.as_mutable(JSONB))
    is_refunded = db.Column(db.Boolean, server_default="false")
    order_id = db.Column(db.Integer, db.ForeignKey('order.id'))
    order = db.relationship('Order', backref='shipments')

我希望有以下类似的东西,但这不是有效的语法:
orders = db.session.query(Order).options(
    db.subqueryload(Order.shipments).filter(Shipment.is_refunded==False))

一些背景信息:
- 订单到发货是一对多的关系。 - 根据业务规则,尽管可能有许多与订单相关联的发货,但只能有一个“活动”发货。即所有其他发货都将其is_refunded状态设置为True。 - 因此,在迭代所有订单时,我只对“活动”发货感兴趣。 - 子查询加载返回所有发货的标准结果。 - 我正在使用最新稳定版本的SQLAlchemy。
如有任何疑问,请告知。
提前致谢。
1个回答

7
有趣的是,我一直在思考一种简单的“选项”方法来完成这种操作,所以你上面的语法很有吸引力。
然而,目前通过relationship()设置的属性契约仅使用relationship()中设置的条件。没有简单的方法可以在运行时更改条件,同时仍然利用提供的加载器服务。
实现使用自定义条件直接使用加载器服务的两个选项是:要么编写使用连接的查询,然后将其与contains_eager(适用于joinedload,而不是子查询加载)结合使用,要么使用满足所需条件的新relationship()(通过primaryjoin建立)。
另一种subqueryload的选择是自己发出相同的查询,但实际上不使用subqueryload选项。这里的特殊技术是能够用查询结果填充集合,使其不被记录为更改事件。set_committed_value函数用于此,一个示例说明了subqueryload(在其内置之前)的“原始”形式,位于DisjointEagerLoading。对于绝大多数简单情况,针对固定实体自己编写“subqueryload”并不难,该技术在该示例中有所说明。

非常感谢!我希望这个功能能够在不久的将来被添加到 SQA 核心中,但目前手动在关系上设置可以很好地工作(尽管有限)。而 DisjointEagerLoading 对于更强大的用例也似乎很有前途。 - Brownbay
@Brownbay建议一个有趣的API,您认为像@zzzeek这样实现它是否可行?我喜欢使用contains_eager的便利性,但是当与多层关系一起工作时,有时使用它太昂贵了。 - jmagnusson
可以做到,但需要付出很大的努力,与此相比,需求并不高。SQLA的开发现在只是勉强跟得上Postgresql不断推出的新功能。 - zzzeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接