Как построить запрос динамически, основываясь на условиях?
Я хочу запросить хранилище данных по диапазону (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 ответа:
Да, это возможно. Из фильтрация по значениям свойств:
Вместо указания всего фильтра запроса в одном выражении, возможно, Вам будет удобнее построить его поэтапно: например:
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