Verwenden von OR in SQLAlchemy

189

Ich habe die Dokumente durchgesehen und kann anscheinend nicht herausfinden, wie eine ODER-Abfrage in SQLAlchemy durchgeführt wird. Ich möchte nur diese Abfrage machen.

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

Sollte so etwas sein

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

Antworten:

321

Aus dem Tutorial :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Bastien Léonard
quelle
72
Beachten Sie, dass dieser Ansatz die Verwendung von Generatoren unterstützt. Wenn Sie also eine lange Liste von Dingen im OP haben, können Sie dies tunfilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
65
@ Robru's Rat ist unnötig ineffizient. Wenn Sie bereits eine Sammlung haben, sollten Sie den in_Operator wie filter(User.name.in_(['Alice', 'Bob', 'Carl']))
folgt verwenden
5
Ah danke ich war mir nicht bewusst, dass sqlalchemy diesen Filter hatte
robru
8
@intgr Das von robru gezeigte Beispiel ist immer noch effizient, wenn Sie anstelle von in_ einen anderen Operator verwenden möchten, beispielsweise den LIKE-Operator.
Lhassan Baazzi
2
@intgr Meine Erfahrung mit Oracle zeigt, dass eine Folge von "ODER" viel schneller ist als die Verwendung von "IN". Außerdem ist "IN" auf einen Satz von ~ 1000 Einträgen beschränkt, während "OR" dies nicht ist.
ga
318

SQLAlchemy Überlastungen die Bit - Operatoren &, |und ~so statt der hässliche und schwer zu lesende Präfix Syntax mit or_()und and_()(wie in Bastiens Antwort ) Sie diese Operatoren verwenden können:

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

Beachten Sie, dass die Klammern aufgrund der Priorität der bitweisen Operatoren nicht optional sind .

Ihre gesamte Anfrage könnte also so aussehen:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
DiebMaster
quelle
8
+1, aber könnten Sie stattdessen die letzten beiden Filterargumente in mehr Klammern setzen und ein &zwischen ihnen und dem ersten verwenden (anstatt einen zweiten filterAufruf zu verwenden), um denselben Effekt zu erzielen?
Chase Sandmann
21
@ChaseSandmann: Ja, das könntest du. Aber wäre es besser lesbar? Nr.
ThiefMaster
1
Es wäre toll, hier als Antwort einen Link zu SQLAlchemy-Dokumenten zu haben!
Cheche
@ThiefMaster Zufall, dass Ihr Alias Dieb enthält und Sie Whitey Bulger in Ihrem Beispiel haben?
TheRealChx101
34

or_() Die Funktion kann bei unbekannter Anzahl von ODER-Abfragekomponenten nützlich sein.

Angenommen, wir erstellen einen REST-Service mit wenigen optionalen Filtern, der den Datensatz zurückgeben soll, wenn einer der Filter true zurückgibt. Wenn andererseits in einer Anforderung kein Parameter definiert wurde, sollte sich unsere Abfrage nicht ändern. Ohne or_()Funktion müssen wir so etwas tun:

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

Mit or_()Funktion kann es umgeschrieben werden in:

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
quelle
1
Sehr hilfreiche Antwort
Ray Toal
3

Das war wirklich hilfreich. Hier ist meine Implementierung für eine bestimmte Tabelle:

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
quelle
Entschuldigung, ich habe einen kleinen Fehler gemacht, ändern Sie die folgende Zeile: query = select ([tableobject]). Where (und _ (* filterargs))
delpozov