SQLAlchemy:按至少一个多对多相关表中的成员身份进行过滤

cor*_*ror 23 python mysql many-to-many sqlalchemy

使用SQLAlchemy 0.7.1和M​​ySQL 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))
Run Code Online (Sandbox Code Playgroud)

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

我正在尝试做的是构建一个SQLAlchemy查询,该查询仅返回属于组列表中至少一个组的用户.

我玩这个in_函数,但这似乎只适用于测试列表中成员资格的标量值.我不是一个SQL编写者,所以我甚至不知道SELECT这需要什么样的声明.

cor*_*ror 40

好的,经过大量的研究,我意识到我自己对SQL术语的无知让我筋疲力尽.我搜索找到属于"至少一个"组列表的用户的解决方案应该是找到属于组列表中"任意"的用户.anySQLAlchemy 的ORM函数完全符合我的需要,如下所示:

session.query(ZKUser).filter(ZKUser.groups.any(ZKGroup.id.in_([1,2,3])))
Run Code Online (Sandbox Code Playgroud)

该代码发出此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)
    )
Run Code Online (Sandbox Code Playgroud)


z0r*_*z0r 10

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

因为any()使用相关子查询,所以与大型目标表进行比较时,其性能与使用连接时的表现差不多.

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

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

这会发出如下SQL:

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