SQLAlchemy:过滤具有深层关系的模型对象?

时间:2015-11-22 19:13:22

标签: python orm sqlalchemy

我有三个具有两个一对多关系的模型对象: Match <-> Slot <-> Player 使用backrefs 'player''match'定义关系。所以我要做的就是找到满足match_filter过滤器的所有匹配项:

slot_filter = or_(Slot.account_id==None,
                  Slot.additional_units!=None)
                # func.count(Slot.player.slots)<=1)

match_filter = and_(Match.human_players==10,
                    Match.game_mode==2,
                   ~Match.slots.any(slot_filter)) 

query = session.query(Match).join(Slot)
query = query.filter(match_filter)
matches = query.all()

问题在于我真的不知道在我的查询中应该在哪里添加Player.slots,以及如何过滤掉至少有一个玩家比n次比赛少(或相等)的比赛。我已经阅读了有关group_byhavingfunc.count方法的内容,但我仍然不了解如何在我的案例中使用它们。

修改我发现了几乎相同的问题:Filter by grandchildren count in SQLAlchemy 但我仍然没有弄清楚如何将其应用于我的人际关系

修改以下是我的解决方案:

subquery = session.query(Player.id).\
           join(Slot).\
           group_by(Player.id).\
           having(func.count(Player.slots) > 1).\
           subquery()

matches =  session.query(Match).\
           join(Slot).\
           join(Player).\
           filter(Player.id.in_(subquery)).\
           filter(match_filter).all()

1 个答案:

答案 0 :(得分:1)

前言:您必须更多地考虑在SQL内工作,而不是在ORM中工作。

如果您想按祖父母过滤,那么 - 如果在SQL工作 - 您显然必须执行JOIN或使用子SELECT

话虽如此,在SQLAlchemy中过滤与在纯SELECT中进行SQL非常接近。

HAVING仅在RDBMS生成结果集之后才适用,因此如果结果集相当大,使用HAVING将非常慢。所以要小心使用。

至于其他选项:

使用JOIN

<强>临

  • 如果您必须通过relationship访问联接对象,则可以使用此选项使用joinedload等预先填充这些对象。

<强>缺点:

  • 要记住的一件事是,如果您执行多对多JOIN,那么您的结果集当然会成倍增加以获得更多行。因此,SQLAlchemy必须阅读更多数据(并可能将其丢弃)。根据您的数据,这会对性能产生影响。
  • 这也是您必须再次使用group_by达到理智count的原因。使用GROUP BY的效果通常低于使用索引的情况。

使用带有correlate

的子SELECT

这些必须使用SQLAlchemy-CORE编写。例如:

subselect = (select([Player.id])
            .select_from(Player.__table__.join(Slot.__table__)
            .where(Player.slot_id == Slot.id)
            .correlate(Slot.__table__))

这不是一个缺点。事实上,我更喜欢以这种方式使用SQLAlchemy,因为我从查询中获得了更可预测的性能。

<强>临

  • 没有像使用JOIN时那样的性能。

<强>缺点:

  • 无法预先加载已加入关系的属性。