Использование ИЛИ в SQLAlchemy

191

Я просмотрел документы и не могу понять, как выполнить запрос OR в SQLAlchemy. Я просто хочу сделать этот запрос.

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

Должно быть что-то вроде

addr = session.query(AddressBook).filter(City == "boston").filter(????)
JiminyCricket
источник

Ответы:

322

Из учебника :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Бастьен Леонар
источник
72
Обратите внимание, что этот подход поддерживает использование генераторов, поэтому, если у вас есть длинный список вещей для ИЛИ, вы можете сделатьfilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
66
Совет @ Робру неоправданно неэффективен. Если у вас уже есть коллекция, используйте in_оператор следующим образом:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr
5
Ах, спасибо, я не знал, что у
sqlalchemy
8
@intgr Пример, показанный robru, все еще эффективен, если вы хотите использовать другой оператор вместо in_, например оператор LIKE.
Лхасан Бааззи
2
@intgr Мой опыт работы с Oracle показывает, что последовательность «ИЛИ» намного быстрее, чем использование «IN». Также «IN» ограничен набором ~ 1000 записей, а «OR» - нет.
га
321

SQLAlchemy перегружает побитовые операторы &, |и ~поэтому вместо уродливого и трудно читаемого синтаксиса префиксов с помощью or_()и and_()(как в ответе Бастьена ) вы можете использовать эти операторы:

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

Обратите внимание, что круглые скобки не являются обязательными из-за приоритета побитовых операторов.

Таким образом, весь ваш запрос может выглядеть так:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
ThiefMaster
источник
8
+1, но не могли бы вы вместо этого заключить два последних аргумента фильтра в большее количество скобок и использовать &между ними и первым (вместо использования второго filterвызова) для того же эффекта?
Чейз Сэндманн
21
@ChaseSandmann: Да, вы могли бы. Но будет ли это более читабельным? Нет
ThiefMaster
1
Было бы здорово иметь ссылку на документацию по SQLAlchemy здесь в ответе!
Чече
@ThiefMaster Совпадение, что в вашем псевдониме содержится вор, и в вашем примере есть Уайти Балджер?
TheRealChx101
36

or_() Функция может быть полезна в случае неизвестного количества компонентов запроса ИЛИ.

Например, давайте предположим, что мы создаем REST-сервис с несколькими необязательными фильтрами, которые должны возвращать запись, если какой-либо из фильтров возвращает true. С другой стороны, если параметр не был определен в запросе, наш запрос не должен изменяться. Без or_()функции мы должны сделать что-то вроде этого:

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

С помощью or_()функции его можно переписать так:

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))
Валар
источник
1
Очень полезный ответ
Рэй Тоал
3

Это было действительно полезно. Вот моя реализация для любой таблицы:

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
источник
Извините, что допустил небольшую ошибку, изменил следующую строку: query = select ([tableobject]). Where (и _ (* filterargs))
delpozov