SQLAlchemy: фильтр по членству хотя бы в одной связанной таблице «многие ко многим» - PullRequest
20 голосов
/ 25 июня 2011

Используя 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 это потребует.

Ответы [ 3 ]

37 голосов
/ 29 июня 2011

Хорошо, после долгих исследований я понял, что мое собственное незнание терминологии SQL сдерживало меня.Мой поиск решения для поиска пользователей, принадлежащих к «хотя бы одной из» списка групп, должен был найти пользователей, принадлежащих к «любому» из списка групп.Функция any ORM из SQLAlchemy делает именно то, что мне нужно, например:

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)
    )
8 голосов
/ 21 июля 2017

Согласно документам для 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)
0 голосов
/ 25 июня 2011

Вы можете использовать in_:

session.query(ZKUser).filter(ZKGroup.id.in_([1,2])).all()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...