SqlAlchemy:检查一个对象是否在任何关系中(或_(object.relationship1.contains(otherObject),object.relationship2.contains(otherObject))

6

假设我有一个像这样的类:

class Foo(declarativeBase):
     bars1 = relationship(Bar.Bar, secondary=foos_to_bars1, collection_class=set())
     bars2 = relationship(Bar.Bar, secondary=foos_to_bars2, collection_class=list())

每个关系都给我一些带有特定条件的“Bar”实例。在某个时刻,我想获取任何一个关系中具有“bar”(Bar.Bar实例)的“Foo”实例。

如果我尝试执行以下操作:

def inAnyBar(bar)
   query(Foo).filter(or_(Foo.bars1.contains(bar), Foo.bars2.contains(bar)).all()

我得到了一个空结果。

看起来(对我来说)像是我在执行以下操作:

query(Foo).join(Foo.bars1).filter(Foo.bars1.contains(bar)).\
join(Foo.bars2).filter(Foo.bars1.contains(bar))

由于Foo.bars1不包含bar,第二个过滤器返回空结果。
我已经找到了使用子查询的解决方法(每个连接+过滤器在一个子查询中,然后对所有子查询进行or_操作),但我想知道是否有更好的方法来处理它...
我找到了这个链接:http://techspot.zzzeek.org/2008/09/09/selecting-booleans/,它可以做到我想做的事情,但它适用于SqlAlchemy 0.5,我(几乎)确定在SqlAlchemy 0.6.6中有一种“更简洁”的方法来处理它。
谢谢!
1个回答

4
你是正确的,session.query(Foo).filter(Foo.bars1.contains(bar)|Foo.bars2.contains(bar)) 会生成如下 SQL:
SELECT "Foo".id AS "Foo_id" 
FROM "Foo", foos_to_bars1 AS foos_to_bars1_1, foos_to_bars2 AS foos_to_bars2_1 
WHERE "Foo".id = foos_to_bars1_1.foo AND ? = foos_to_bars1_1.bar OR 
"Foo".id = foos_to_bars2_1.foo AND ? = foos_to_bars2_1.bar

secondary表中有一个为空时,会返回错误的结果。似乎是SQLAlchemy的一个bug。然而,将contains()替换为any()可以解决这个问题(它使用EXISTS子查询):

session.query(Foo).filter(Foo.bars1.any(id=bar.id)|Foo.bars2.any(id=bar.id))

同时,您可以显式指定外连接:

Bar1 = aliased(Bar)
Bar2 = aliased(Bar)
session.query(Foo).outerjoin((Bar1, Foo.bars1)).outerjoin((Bar2, Foo.bars2))\
    .filter((Bar1.id==bar.id)|(Bar2.id==bar.id))

SQLA除了将“contains()”运算符的复杂度大幅增加以在m2m存在时使用EXISTS(执行效率极差)之外,几乎无能为力。因此,any()和has()在这个问题上几乎没有用处。如果有建议,请随时重新打开#2177工单;否则,暂时关闭该工单。 - zzzeek
@zzzeek 只使用 OUTER JOIN 不是一个选项吗? - Denis Otkidach
确实,它可以使用session.query(Foo).filter(Foo.bars1.any(id=bar.id)|Foo.bars2.any(id=bar.id))。我会检查outerjoin版本。非常感谢。 - Savir

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