假设这些模型:
class A(Base):
...
targets = relationship("B", back_populates='a')
...
class B(Base):
...
a_id = Column(Integer, ForeignKey('a.id'))
a = relationship("A", back_populates='targets')
attr = Column(ENUM('a', 'b', 'c', name='options'), default='a')
...
如果所有相关的hybrid_attribute.expression
在A
中具有B
属性值,我如何编写attr
返回所有('b', 'c')
的True? (如果没有相关的B
,也应该返回False。)
这使我半途而废:
# hybrid_attribute expression in A model
@example_attr.expression
def example_attr(cls):
return case(
[
(
B.attr.in_(('b','c')),
True
)
],
else_=False
)
但是如何将它们按A.id
分组,如果有任何相关的B
是True
,那么该example_attr
行的A
列值就是True
?
我希望top能够做到:session.query(A).filter(A.example_attr.is_(True)).all()
编辑1:
此SQL似乎提供了所需的结果:
select a.id, coalesce(bool_or(a_id_b_attr.status), false)
from a left outer join (
select b.a_id as a_id, b.attr in ('b', 'c') as status from b
) as a_id_b_attr
on a.id = a_id_b_attr.a_id group by a.id;
但是我在引用子查询中的别名字段时遇到了一个小问题:
sub = db.session.query(B.a_id.label('a_id'), B.attr.in_(('b', 'c')).label('status')).subquery()
db.session.query(
A.id,
db.func.coalesce(
db.func.bool_or(sub.status),
False
)
).\
outerjoin(sub, A.id == sub.key_id).\
group_by(A.id)
抛出AttribubteError
的原因是sub
没有任何别名属性。
答案 0 :(得分:3)
我如何编写
hybrid_attribute.expression
来为所有As返回True
?如果任何相关B在('b', 'c')
中具有attr属性值? (如果没有相关的B,它也应该返回False
。)
您可以使用EXISTS子查询表达式:
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
targets = relationship("B", back_populates='a')
@hybrid_property
def example_attr(self):
return any(b.attr in {'b', 'c'} for b in self.targets)
@example_attr.expression
def example_attr(cls):
return exists().\
where(and_(B.a_id == cls.id,
B.attr.in_(['b', 'c']))).\
correlate(cls)
class B(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
a_id = Column(Integer, ForeignKey('a.id'))
a = relationship("A", back_populates='targets')
attr = Column(ENUM('a', 'b', 'c', name='options'), default='a')
这不依赖任何分组,而是相关性,可以直接在所需的查询中使用:
# Note that comparing booleans with booleans is a bit redundant
session.query(A).filter(A.example_attr).all()
Postgresql能够使用相关的EXISTS子查询表达式将(某些)查询重写为semijoins,因此有关相关性的常规性能注释可能不适用。
如果您想通过分组来解决这个问题,可以使用bool_or
和显式联接:
session.query(A).\
join(B).\
group_by(A.id).\
having(func.bool_or(B.attr.in_(['b', 'c']))).\
all()
Postgresql允许在此分组查询中选择A
的所有列,即使它们不是聚合表达式,因为它们在功能上依赖于分组列(换句话说,A.id
确定了所选列)列。