Utilisation de OR dans SQLAlchemy

191

J'ai parcouru la documentation et je n'arrive pas à trouver comment faire une requête OR dans SQLAlchemy. Je veux juste faire cette requête.

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

Devrait être quelque chose comme

addr = session.query(AddressBook).filter(City == "boston").filter(????)
Jiminy Cricket
la source

Réponses:

322

À partir du tutoriel :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Bastien Léonard
la source
72
Notez que cette approche prend en charge l'utilisation de générateurs, donc si vous avez une longue liste de choses à faire, vous pouvez le fairefilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
66
Les conseils de @ Robru sont inefficaces. Si vous avez déjà une collection, vous devez utiliser l' in_opérateur comme celui-ci:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr
5
Ah merci, je ne savais pas que sqlalchemy avait ce filtre
robru
8
@intgr L'exemple montré par robru est toujours efficace, si vous souhaitez utiliser un autre opérateur au lieu de in_, par exemple l'opérateur LIKE.
Lhassan Baazzi
2
@intgr Mon expérience avec Oracle montre qu'une séquence de "OU" est bien plus rapide que d'utiliser "IN". De plus, "IN" est limité à un ensemble d'environ 1000 entrées, alors que "OR" ne l'est pas.
ga
321

SQLAlchemy surcharge les opérateurs de bits &, |et ~donc au lieu de la syntaxe de préfixe laide et difficile à lire avec or_()et and_()(comme dans la réponse de Bastien ), vous pouvez utiliser ces opérateurs:

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

Notez que les parenthèses ne sont pas facultatives en raison de la priorité des opérateurs de bits.

Ainsi, toute votre requête pourrait ressembler à ceci:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
ThiefMaster
la source
8
+1, mais pourriez-vous à la place mettre les deux derniers arguments de filtre dans plus de parenthèses et utiliser un &entre eux et le premier (plutôt que d'utiliser un deuxième filterappel) pour le même effet?
Chase Sandmann
21
@ChaseSandmann: Oui, vous pourriez. Mais serait-ce plus lisible? No.
ThiefMaster
1
Ce serait génial d'avoir un lien vers la documentation SQLAlchemy ici en réponse!
Cheche
@ThiefMaster Coïncidence que votre alias contient voleur et que vous avez Whitey Bulger dans votre exemple?
TheRealChx101
36

or_() La fonction peut être utile en cas de nombre inconnu de composants de requête OR.

Par exemple, supposons que nous créons un service REST avec quelques filtres facultatifs, qui devrait renvoyer un enregistrement si l'un des filtres renvoie true. D'un autre côté, si le paramètre n'a pas été défini dans une requête, notre requête ne devrait pas changer. Sans or_()fonction, nous devons faire quelque chose comme ceci:

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

Avec la or_()fonction, il peut être réécrit en:

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
la source
1
Réponse très utile
Ray Toal
3

Cela a été vraiment utile. Voici mon implémentation pour n'importe quelle table donnée:

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
la source
Désolé j'ai fait une petite erreur, chanhe la ligne suivante: query = select ([tableobject]). Where (and _ (* filterargs))
delpozov