Використання АБО в SQLAlchemy


191

Я переглянув документи і, здається, не можу дізнатися, як зробити АБО-запит у SQLAlchemy. Я просто хочу зробити цей запит.

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

Повинно бути щось на кшталт

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

Відповіді:


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 мав цей фільтр
robru

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

8
+1, але чи можете ви замість цього загортати останні два аргументи фільтра в більше круглих дужок та використовувати &між ними та першим (а не використовувати другий filterвиклик) для того ж ефекту?
Чейз Сандман

21
@ChaseSandmann: Так, ти міг. Але чи було б це читабельніше? №
ThiefMaster

1
Було б чудово, щоб у відповідь було посилання на документи SQLAlchemy!
Чече

@TheadMaster Збіг обставин, що у вашому псевдонімі є злодій, і у вас є приклад Whitey Bulger?
TheRealChx101

35

or_() Функція може бути корисна у випадку невідомої кількості компонентів запиту АБО.

Наприклад, припустимо, що ми створюємо послугу REST з кількома необов'язковими фільтрами, які повинні повертати запис, якщо будь-який з фільтрів повертає справжнє. З іншого боку, якщо параметр не був визначений у запиті, наш запит не повинен змінюватися. Без 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
Дуже корисна відповідь
Ray Toal

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)

Вибачте, що я зробив невелику помилку, пропонуйте наступний рядок: query = select ([tableobject]). Де (і _ (* filterargs))
delpozov
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.