2011-05-25 5 views
5

Digamos que tengo una clase como esta:sqlalchemy: Comprobar si un objeto se encuentra en cualquier relación (or_ (object.relationship1.contains (otherObject), object.relationship2.contains (otherObject))

class Foo(declarativeBase): 
    bars1 = relationship(Bar.Bar, secondary=foos_to_bars1, collection_class=set()) 
    bars2 = relationship(Bar.Bar, secondary=foos_to_bars2, collection_class=list()) 

(Cada una de las relaciones me da "Barras" con ciertas condiciones). En cierto punto, quiero obtener instancias de "Foo" s que tengan una "barra" (instancia de Bar.Bar) en cualquiera de las relaciones .

Si trato de hacer:

def inAnyBar(bar) 
    query(Foo).filter(or_(Foo.bars1.contains(bar), Foo.bars2.contains(bar)).all() 

Obtengo un resultado vacío.

Parece (para mí) que estoy haciendo algo como:

query(Foo).join(Foo.bars1).filter(Foo.bars1.contains(bar)).\ 
join(Foo.bars2).filter(Foo.bars1.contains(bar)) 

Desde Foo.bars1 no contiene barras, el segundo filtro da resultados vacíos.

que he sido capaz de encontrar una solución con subconsultas (cada unión + filtro en una sub consulta, a continuación, or_ todas las subconsultas) pero me gustaría saber si hay una mejor manera de hacerlo ...

encontré esto: http://techspot.zzzeek.org/2008/09/09/selecting-booleans/

que hace lo que yo quiero hacer, pero es de 0,5 sqlalchemy y estoy (casi) seguro que hay una manera "más limpia" de hacerlo con sqlalchemy 0.6.6

¡Gracias!

Respuesta

3

tiene usted razón, session.query(Foo).filter(Foo.bars1.contains(bar)|Foo.bars2.contains(bar)) produce el siguiente SQL:

SELECT "Foo".id AS "Foo_id" 
FROM "Foo", foos_to_bars1 AS foos_to_bars1_1, foos_to_bars2 AS foos_to_bars2_1 
WHERE "Foo".id = foos_to_bars1_1.foo AND ? = foos_to_bars1_1.bar OR 
"Foo".id = foos_to_bars2_1.foo AND ? = foos_to_bars2_1.bar 

que devuelve resultado incorrecto cuando una de las mesas secondary está vacía. Parece un error en SQLAlchemy. Sin embargo la sustitución de con any() solucionado el problema (que utiliza EXISTS):

session.query(Foo).filter(Foo.bars1.any(id=bar.id)|Foo.bars2.any(id=bar.id)) 

También puede especificar OUTER JOIN explícitamente:

Bar1 = aliased(Bar) 
Bar2 = aliased(Bar) 
session.query(Foo).outerjoin((Bar1, Foo.bars1)).outerjoin((Bar2, Foo.bars2))\ 
    .filter((Bar1.id==bar.id)|(Bar2.id==bar.id)) 
+0

No hay mucho SQLA puede hacer otra cosa que aumentar drásticamente la complejidad de la El operador "contains()" usa EXISTS cuando m2m está presente, lo que funciona de manera horrible. any() y has() son casi inútiles por este motivo. No dude en volver a abrir el ticket n. ° 2177 con una propuesta; de lo contrario, estará cerrado por el momento. – zzzeek

+0

@zzzeek ¿El solo uso de OUTER JOIN no es una opción? –

+0

De hecho, funciona con session.query (Foo) .filter (Foo.bars1.any (id = bar.id) | Foo.bars2.any (id = bar.id)). Comprobaré la versión de outerjoin. Muchas gracias – BorrajaX

Cuestiones relacionadas