Как построить запрос динамически, основываясь на условиях?


Я хочу запросить хранилище данных по диапазону (0-4) возможных критериев, основываясь на запросе пользователя. Запросы в NDB строятся следующим образом:

query = Account.query(Account.userid >= 40, Account.userid < 50)

Есть ли способ, которым я мог бы сделать что-то вроде:

myfilter = []
myfilter.push('Account.userid >= 40')
myfilter.push('Account.userid < 50')
myfilter.push('Account.name == "John"')
query = Account.query(*myfilter)

Может быть от 0 до 4 аргументов фильтра, в зависимости от условий. Мое предположение (которое может быть неверным) состоит в том, что более оптимально пропустить фильтр, чем иметь всеохватывающий (напр. Счет.userid == *) если в этом нет необходимости.

Я знаю, что вы можете цеплять фильтры но так как объекты запроса неизменяемы, не уверен, что это поможет мне.

2 3

2 ответа:

Да, это возможно. Из фильтрация по значениям свойств:

Вместо указания всего фильтра запроса в одном выражении, возможно, Вам будет удобнее построить его поэтапно: например:

query1 = Account.query()  # Retrieve all Account entitites
query2 = query1.filter(Account.userid >= 40)  # Filter on userid >= 40
query3 = query2.filter(Account.userid < 50)  # Filter on userid < 50 too

query3 эквивалентна переменной query из предыдущей образец. Обратите внимание, что объекты запроса являются неизменяемыми, поэтому построение query2 не влияет на query1 и построение query3 не влияет на query1 или query2.

Вы можете использовать такой метод построения инкрементных запросов для условного добавления фильтров по мере необходимости. Например (это предполагает общее и между необязательными условиями):

query = Account.query()  # Retrieve all Account entitites
loggin.error(query)

if filter_by_userid:
    query = query.filter(Account.userid >= 40, Account.userid < 50)
    loggin.error(query)

if filter_by_username:
    query = query.filter(Account.name == "John")
    loggin.error(query)

loggin.error('Final: %s' % query)
Приведенный выше фрагмент кода специально использует преимущества имутативности объекта запроса, каждое назначение переменной queryфактически сохраняет новый объект запроса , полученный путем применения соответствующего фильтра. Подтверждается соответствующими сообщениями журнала.

Есть более элегантный способ сделать это. И кстати более динамично:

def build_query_by(ndb_class, filters, sorts):
    """
    ndb_class: the ndb model class to query
    filters: a list of tuples of properties, operations and values
    sorts: a list of tuples of properties and order symbol
    """
    q = ndb_class.query()
    for prop, operation, value in filters:
        if operation == '==':
            q = q.filter(getattr(ndb_class, prop) == value)
        elif operation == '>=':
            q = q.filter(getattr(ndb_class, prop) >= value)
        elif operation == '<=':
            q = q.filter(getattr(ndb_class, prop) <= value)
        # more operations...

    for prop, symbol in sorts:
        if symbol == '-':
            q = q.order(-getattr(ndb_class, prop))
        else:
            q = q.order(getattr(ndb_class, prop))
    return q