Usando OR en SQLAlchemy

191

He revisado los documentos y parece que no puedo encontrar cómo hacer una consulta OR en SQLAlchemy. Solo quiero hacer esta consulta.

SELECT address FROM addressbook WHERE city='boston' AND (lastname='bulger' OR firstname='whitey')

Debería ser algo como

addr = session.query(AddressBook).filter(City == "boston").filter(????)
JiminyCricket
fuente

Respuestas:

322

Del tutorial :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Bastien Léonard
fuente
72
Tenga en cuenta que este enfoque admite el uso de generadores, por lo que si tiene una larga lista de cosas para OR, puede hacerlofilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
66
El consejo de @ Robru es innecesariamente ineficiente. Si ya tiene una colección, debe utilizar el in_operador de esta manera:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr
55
Ah, gracias, no sabía que sqlalchemy tenía ese filtro
robru 02 de
8
@intgr El ejemplo mostrado por robru sigue siendo eficiente, si desea utilizar otro operador en lugar de in_, por ejemplo, el operador LIKE.
Lhassan Baazzi
2
@intgr Mi experiencia con Oracle muestra que una secuencia de "OR" es mucho más rápida que usar "IN". Además, "IN" está limitado a un conjunto de ~ 1000 entradas, mientras que "OR" no lo está.
ga
321

SQLAlchemy sobrecarga los operadores de bits &, |y ~así que en vez de lo feo y de difícil lectura prefijo de sintaxis con or_()y and_()(como en la respuesta de Bastien ) se pueden utilizar estos operadores:

.filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

Tenga en cuenta que los paréntesis no son opcionales debido a la precedencia de los operadores bit a bit.

Entonces toda su consulta podría verse así:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
ThiefMaster
fuente
8
+1, pero ¿podría envolver los dos últimos argumentos de filtro entre paréntesis y usar un &entre ellos y el primero (en lugar de usar una segunda filterllamada) para el mismo efecto?
Chase Sandmann
21
@ChaseSandmann: Sí, podrías. ¿Pero sería más legible? No.
ThiefMaster
1
¡Sería genial tener un enlace a los documentos de SQLAlchemy aquí en respuesta!
Cheche
@ThiefMaster ¿Coincidencia de que su alias contiene ladrón y que tiene Whitey Bulger en su ejemplo?
TheRealChx101
36

or_() La función puede ser útil en caso de un número desconocido de componentes de consulta OR.

Por ejemplo, supongamos que estamos creando un servicio REST con pocos filtros opcionales, que debería devolver el registro si alguno de los filtros devuelve verdadero. Por otro lado, si el parámetro no se definió en una solicitud, nuestra consulta no debería cambiar. Sinor_() función debemos hacer algo como esto:

query = Book.query
if filter.title and filter.author:
    query = query.filter((Book.title.ilike(filter.title))|(Book.author.ilike(filter.author)))
else if filter.title:
    query = query.filter(Book.title.ilike(filter.title))
else if filter.author:
    query = query.filter(Book.author.ilike(filter.author))

Con la or_()función se puede reescribir a:

query = Book.query
not_null_filters = []
if filter.title:
    not_null_filters.append(Book.title.ilike(filter.title))
if filter.author:
    not_null_filters.append(Book.author.ilike(filter.author))

if len(not_null_filters) > 0:
    query = query.filter(or_(*not_null_filters))
Valar
fuente
1
Respuesta muy útil
Ray Toal
3

Esto ha sido realmente útil. Aquí está mi implementación para cualquier tabla dada:

def sql_replace(self, tableobject, dictargs):

    #missing check of table object is valid
    primarykeys = [key.name for key in inspect(tableobject).primary_key]

    filterargs = []
    for primkeys in primarykeys:
        if dictargs[primkeys] is not None:
            filterargs.append(getattr(db.RT_eqmtvsdata, primkeys) == dictargs[primkeys])
        else:
            return

    query = select([db.RT_eqmtvsdata]).where(and_(*filterargs))

    if self.r_ExecuteAndErrorChk2(query)[primarykeys[0]] is not None:
        # update
        filter = and_(*filterargs)
        query = tableobject.__table__.update().values(dictargs).where(filter)
        return self.w_ExecuteAndErrorChk2(query)

    else:
        query = tableobject.__table__.insert().values(dictargs)
        return self.w_ExecuteAndErrorChk2(query)

# example usage
inrow = {'eqmtvs_id': eqmtvsid, 'datetime': dtime, 'param_id': paramid}

self.sql_replace(tableobject=db.RT_eqmtvsdata, dictargs=inrow)
delpozov
fuente
Lo siento, cometí un pequeño error, cambia la siguiente línea: query = select ([tableobject]). Where (and _ (* filterargs))
delpozov