【问题标题】:Load only subset of joined rows in SQLAlchemy ORM在 SQLAlchemy ORM 中仅加载连接行的子集
【发布时间】:2015-02-23 16:28:48
【问题描述】:

我定义了以下映射类

class A(Base):
    __tablename__ = "a"
    id = sqla.Column(sqla.Integer, primary_key = True)
    number_a = sqla.Column(sqla.Integer)
    b_collection = relationship('B', backref = backref('a'))

class B(Base):
    __tablename__ = "b"
    id = sqla.Column(sqla.Integer, primary_key = True)
    id_a = sqla.Column(sqla.Integer, sqla.ForeignKey('a.id'))
    number_b = sqla.Column(sqla.Integer)

数据库中存储了这些对象:

a1 = A(number_a = 1)
a2 = A(number_a = 2)
b1 = B(number_b = 50, a = a2)
b2 = B(number_b = 51, a = a2)

我需要的是通过仅发出一个查询来查询关系,以便它仅通过该查询的结果预加载b_collection

result = session.query(A).join(B).filter(B.number_b == 51).all()

所以现在我希望result[0].b_collection 尊重过滤表达式并且不包括B 实例with number_b = 50。所以我的预期输出是:

for a in result:
    for b in a.b_collection:
        print(b.number_b)

应该是:

51

但是遍历a.b_collection 总是会发出一个新查询并将B 对象加载到b_collection 中,因此结果是

50
51

这是我不想得到的。

如何强制原始查询加载所有对象并根据给定的过滤条件填充关系集合?

感谢您的回复。

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    使用contains_eager 执行此操作,但请注意您在欺骗 SQL Alchemy,因此不要重用此UnitOfWork 来执行常规关系相关任务:

    result = (
        session.query(A)
        .join(B)
        .filter(B.number_b == 51)
        .options(contains_eager(A.b_collection))  # this is the key
        ).all()
    

    【讨论】:

    • 谢谢,这行得通!它在什么意义上欺骗?有什么危害?
    • 你在欺骗session 认为你加载关系A.b_collection。所以如果你对它执行操作,它们并不能真正代表后端数据库的状态。
    • 是的,没错。明白了。感谢您的警告和解决方案。
    • 再问一点:可能有数千个 A 对象与数千个 B 对象相关。想象一下对所有这些加入的对象进行操作并满足一些 sql 表达式(通过AB 的列限制结果集)然后无需欺骗,会话将始终知道与每个查询的@相关的所有B对象987654331@ 对象和对Bs 的过滤将丢失。 “欺骗”对我来说听起来像是“我应该避免的坏事”。你的意思是我不应该这样做还是只是一个“小心”的说明?有什么方法可以做到这一点而不欺骗?
    • 我的意思是:请注意。如果您有很多对象并且不想加载所有对象,您可以考虑在关系定义中使用lazy='dynamic' 作为处理关系的一种方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-14
    • 2021-08-30
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 2012-07-31
    相关资源
    最近更新 更多