Python 过滤SQLAlchemy查询结果对象';这是一对多属性

Python 过滤SQLAlchemy查询结果对象';这是一对多属性,python,orm,sqlalchemy,filtering,one-to-many,Python,Orm,Sqlalchemy,Filtering,One To Many,假设我有两个对象,有一对多的关系,比如 class Parent(): //id, other cols, etc children = relationship("Child", backref="parent") class Child(): parent_id = Column(Integer, ForeignKey("parent.id") child_type = Column(Enum("a","b")) session.query(Parent)

假设我有两个对象,有一对多的关系,比如

class Parent():
    //id, other cols, etc
    children = relationship("Child", backref="parent")

class Child():
    parent_id = Column(Integer, ForeignKey("parent.id")
    child_type = Column(Enum("a","b"))
session.query(Parent).join(Parent.children).filter(Child.child_type == "a")
现在,我想查询父对象,但要按child_类型筛选它们的子对象,例如

class Parent():
    //id, other cols, etc
    children = relationship("Child", backref="parent")

class Child():
    parent_id = Column(Integer, ForeignKey("parent.id")
    child_type = Column(Enum("a","b"))
session.query(Parent).join(Parent.children).filter(Child.child_type == "a")

这只返回包含所有子级的父级,基本上不考虑过滤器。这个结果是可能的还是我也必须查询Child?

您试图在一个查询中得到两个答案。你可以询问所有有a型孩子的父母,也可以询问所有a型孩子。在第一种情况下,您必须再次筛选子项,如果您想要相应的子项,在第二种情况下,您可以简单地获得相应的父项。但是哪种方法是正确的,取决于您试图解决的进一步问题。

实际上,您的查询添加了一个联接和一个筛选器,但只返回父实例。事实上,那些
实例,它们至少有一个
类型
a

然后,当您访问每个父级上的
.children
时,将发出一条新的SQL语句,并加载该父级的所有子级。您可以在内存中再次应用过滤器,或创建自己的查询,而不依赖关系导航(注释掉),如下所示:

# select *only* those parents who have at least one child of type "a"
parents = session.query(Parent).join(Parent.children).filter(Child.child_type == "a")
for p in parents:
    # 1. in-memory filter: now select only type "a" children for each parent
    children_a = [c for c in p.children if c.child_type == 'a']
    # 2. custom query: now select only type "a" children for each parent
    # children_a = session.query(Child).with_parent(p).filter(Child.child_type == "a")

    print("AAA", p)
    for c in children_a:
        print("AAA ..", c)
下面显示了在一个查询中执行此操作的方法,但要小心,因为您实际上是在告诉sqlalchemy您为家长加载了所有子项。您可以将此方法用于执行查询然后放弃/回收会话的场景:

# select all parents, and eager-load children of type "a"
parents = (session.query(Parent)
        .join(Parent.children).filter(Child.child_type == "a")
        # make SA think we loaded all *parent.children* collection
        .options(contains_eager('children'))
        )

for p in parents:
    children_a = p.children # now *children* are *incorrectly* filtered
    print("BBB", p)
    for c in children_a:
        print("BBB ..", c)