Usando OR no SQLAlchemy

191

Eu olhei através dos docs e eu não consigo descobrir como fazer um OR consulta no SQLAlchemy. Eu só quero fazer essa consulta.

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

Deve ser algo como

addr = session.query(AddressBook).filter(City == "boston").filter(????)
Grilo Falante
fonte

Respostas:

322

Do tutorial :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Bastien Léonard
fonte
72
Observe que essa abordagem suportes usando geradores, por isso, se você tem uma longa lista de coisas a OR, você pode fazerfilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
66
@ O conselho de Robru é desnecessariamente ineficiente. Se você já possui uma coleção, deve usar o in_operador assim:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr 30/09/16
5
Ah, graças i não estava ciente sqlalchemy teve que filtram
robru
8
@intgr O exemplo mostrado pelo robru ainda é eficiente, se você quiser usar outro operador em vez de in_, por exemplo, o operador LIKE.
Lhassan Baazzi
2
@intgr Minha experiência com o Oracle mostra que uma sequência de "OR" s é muito mais rápida do que usar "IN". Além disso, "IN" é limitado a um conjunto de ~ 1000 entradas, enquanto "OR" não é.
ga
321

SQLAlchemy sobrecarrega os operadores bit a bit &, |e ~assim, em vez do feio e difícil de ler prefixo sintaxe com or_()e and_()(como na resposta de Bastien ) você pode usar esses operadores:

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

Observe que os parênteses não são opcionais devido à precedência dos operadores bit a bit.

Portanto, toda a sua consulta pode ficar assim:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
ThiefMaster
fonte
8
+1, mas você poderia colocar os dois últimos argumentos de filtro entre parênteses e usar um &entre eles e o primeiro (em vez de usar uma segunda filterchamada) para o mesmo efeito?
Página
21
@ PhaseSandmann: Sim, você poderia. Mas seria mais legível? Não.
ThiefMaster 14/08
1
Seria ótimo ter um link para os documentos SQLAlchemy aqui em resposta!
Cheche
@ThiefMaster Coincidência de que seu apelido contenha ladrão e você tenha Whitey Bulger no seu exemplo?
TheRealChx101 19/10/19
36

or_() A função pode ser útil em caso de número desconhecido de componentes de consulta OR.

Por exemplo, suponha que estamos criando um serviço REST com poucos filtros opcionais, que devem retornar registro se algum dos filtros retornar verdadeiro. Por outro lado, se o parâmetro não foi definido em uma solicitação, nossa consulta não deve mudar. Sem or_()função, devemos fazer algo assim:

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

Com a or_()função, pode ser reescrita para:

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
fonte
1
Resposta muito útil
Ray Toal
3

Isso tem sido realmente útil. Aqui está minha implementação para qualquer tabela:

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
fonte
Desculpe eu cometi um pequeno erro, chanhe seguinte linha: query = selecionar ([tableobject]), onde (e _ (* filterargs)).
delpozov