【问题标题】:SQLAlchemy: filter by membership in at least one many-to-many related tableSQLAlchemy:按至少一个多对多相关表中的成员资格过滤
【发布时间】:2011-09-22 10:20:52
【问题描述】:

使用 SQLAlchemy 0.7.1 和 MySQL 5.1 数据库,我建立了一个多对多关系,如下所示:

user_groups = Table('user_groups', Base.metadata,
    Column('user_id', String(128), ForeignKey('users.username')),
    Column('group_id', Integer, ForeignKey('groups.id'))
)

class ZKUser(Base, ZKTableAudit):
    __tablename__ = 'users'

    username   = Column(String(128), primary_key=True)
    first_name = Column(String(512))
    last_name  = Column(String(512))

    groups = relationship(ZKGroup, secondary=user_groups, backref='users')

class ZKGroup(Base, ZKTableAudit):
    __tablename__ = 'groups'

    id          = Column(Integer, primary_key=True)
    name        = Column(String(512))

用户可以属于多个组,组可以包含多个用户。

我要做的是构建一个 SQLAlchemy 查询,该查询仅返回属于组列表中至少一个组的用户。

我玩过in_ 函数,但这似乎只适用于测试列表中成员资格的标量值。我不太会写 SQL,所以我什至不知道这需要什么样的SELECT 语句。

【问题讨论】:

    标签: python mysql many-to-many sqlalchemy


    【解决方案1】:

    好的,经过大量研究,我意识到是我自己对 SQL 术语的无知阻碍了我。我寻找属于“至少一个”组列表的用户的解决方案应该是找到属于“任何”组列表的用户。来自 SQLAlchemy 的 any ORM 函数正是我所需要的,就像这样:

    session.query(ZKUser).filter(ZKUser.groups.any(ZKGroup.id.in_([1,2,3])))
    

    该代码发出此 SQL(在 MySQL 5.1 上):

    SELECT * FROM users 
    WHERE EXISTS (
        SELECT 1 FROM user_groups, groups 
        WHERE users.id = user_groups.contact_id 
            AND groups.id = user_groups.group_id 
            AND groups.id IN (%s, %s, %s)
        )
    

    【讨论】:

    • 如何在所有多对多相关表中按成员身份进行过滤?
    • 您的答案引用了ZKContactcontacts,但这些不在您的问题代码中。你是说 ZKUser 吗?
    • 否决投票答案,它使用的关键字与问题不同,很难遵循可能的解决方案。
    • @Drachenfels 我修复了命名不一致问题。
    • 太棒了! SQLAlchemy 继续令人震惊。
    【解决方案2】:

    根据the docs for any,如果您使用显式join,查询将运行得更快:

    由于 any() 使用关联子查询,因此与大型目标表相比,它的性能不如使用连接的性能好。

    在您的情况下,您可以执行以下操作:

    users = (
        session.query(ZKUser)
        .join(user_groups)
        .filter(user_groups.columns.group_id.in_([1, 2, 3]))
    )
    

    这会发出如下 SQL:

    SELECT *
    FROM users
    JOIN user_groups ON users.id = user_groups.user_id 
    WHERE user_groups.group_id IN (1, 2, 3)
    

    【讨论】:

      【解决方案3】:

      你可以使用in_:

      session.query(ZKUser).filter(ZKGroup.id.in_([1,2])).all()
      

      【讨论】:

      • 我会在周末后回到办公室时试一试。这看起来很有希望。另外,我喜欢我的班级名称和你的用户名的巧合。 :) 我的“ZK”代表 Zookeeper,我的应用程序的名称。它与 MailChimp 的 API 接口。
      • 不幸的是,这不起作用。我认为这是因为这并没有进行实际的联接,因此由此发出的 SQL 实际上并没有过滤掉不匹配的用户。我查看了 SQLAlchemy 上的文档,了解在多对多表上进行连接,但我完全迷失了。
      • 你可以试试session.query(ZKUser).join(ZKGroup).filter(..)。它应该在 0.7.2 中工作,afaik。
      • NotImplementedError: in_() not yet supported for relationships. For a simple many-to-one, use in_() against the set of foreign key values. SQLAlchemy 1.3.18
      猜你喜欢
      • 2014-03-28
      • 2011-09-13
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 2021-12-17
      • 2019-03-26
      • 1970-01-01
      相关资源
      最近更新 更多