2011-06-25 22 views
16

Usando SQLAlchemy 0.7.1 y una base de datos MySQL 5.1 relacionado, Tengo una relación muchos-a-muchos relación establecido de la siguiente manera:SQLAlchemy: filtro por la pertenencia a al menos uno-muchos-a-muchos mesa

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)) 

Los usuarios pueden pertenecer a múltiples grupos, y los grupos pueden contener varios usuarios.

Lo que intento hacer es crear una consulta SQLAlchemy que devuelva solo a los Usuarios que pertenecen a al menos un Grupo de una lista de Grupos.

Jugué con la función in_, pero eso solo parece funcionar para probar valores escalares para la membresía en una lista. No soy muy escritor de SQL, así que ni siquiera sé qué tipo de declaración SELECT requeriría.

Respuesta

29

bien, después de mucha investigación, me di cuenta de que era mi propia ignorancia de la terminología de SQL que me estaba frenando. Mi búsqueda de una solución para encontrar usuarios que pertenezcan a "al menos uno de" la lista de grupos debería haber sido encontrar usuarios que pertenezcan a "cualquiera" de la lista de grupos. La función any ORM de SQLAlchemy hace exactamente lo que necesitaba, así:

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

Ese código emite este SQL (en 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) 
    ) 
+0

¿Cómo se filtra por membresía en todas las tablas relacionadas de muchos a muchos? – WebPal

+1

Su respuesta hace referencia a 'ZKContact' y' contacts', pero estos no están en su código en la pregunta. ¿Querías decir ZKUser? –

+0

Abajo la respuesta de votación, está usando palabras clave diferentes a las preguntas, y es difícil seguir cuál es la posible solución. – Drachenfels

0

Puede utilizar in_:

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

Voy a dar a este un tiro cuando vuelva a la oficina después de la fin de semana. Esto parece prometedor. Además, me gusta la coincidencia de mis nombres de clase y su nombre de usuario. :) Mi "ZK" significa Zookeeper, el nombre de mi aplicación. Interactúa con la API de MailChimp. – CoreDumpError

+0

Desafortunadamente, esto no funcionó. Creo que es porque esto no está haciendo una unión real, por lo que el SQL que se emite por esto en realidad no filtra a los usuarios que no coinciden. Revisé los documentos en SQLAlchemy para hacer combinaciones en tablas de muchos a muchos, pero estoy totalmente perdido. – CoreDumpError

+1

Puede intentar hacer 'session.query (ZKUser) .join (ZKGroup) .filter (..)'. Debería funcionar en 0.7.2, afaik. – zeekay

3

Según the docs for any, la consulta se ejecutará más rápido si se utiliza una explícita join lugar:

Debido a que cualquier() utiliza una subconsulta correlacionada, su rendimiento no es tan bueno cuando se compara con grandes tablas de destino como el de usar una unión.

En su caso, usted podría hacer algo como:

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

Este emite SQL como:

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