Динамическое построение фильтров в SQLAlchemy

Я ищу способ динамически создавать фильтры с помощью SQLAlchemy. То есть, учитывая столбец, имя оператора и значение сравнения, создайте соответствующий фильтр.

Я попытаюсь проиллюстрировать пример (это будет использовано для создания API). Допустим, у нас есть следующая модель:

class Cat(Model): id = Column(Integer, primary_key=True) name = Column(String) age = Column(Integer) 

Я хотел бы сопоставлять запросы с фильтрами. Например,

  • /cats?filter=age;eq;3 должен генерировать Cat.query.filter(Cat.age == 3)

  • /cats?filter=age;in;5,6,7&filter=id;ge;10 должен генерировать Cat.query.filter(Cat.age.in_([5, 6, 7])).filter(Cat.id >= 10)

Я осмотрелся, чтобы посмотреть, как это было сделано, но не смог найти способ, который не включал бы вручную сопоставление каждого имени оператора с компаратором или что-то подобное. Например, Flask-Restless хранит словарь всех поддерживаемых операций и сохраняет соответствующие лямбда-функции ( код здесь ).

Я искал в документах SQLAlchemy и нашел два потенциальных вывода, но ни один из них не казался удовлетворительным:

  • используя Column.like , Column.in_ …: эти операторы доступны непосредственно в столбце, который упростит использование getattr но некоторые из них все еще отсутствуют ( == , > и т. д.).

  • используя Column.op : например, Cat.name.op('=')('Hobbes') но это, похоже, не работает для всех операторов (а именно).

Есть ли чистый способ сделать это без lambda функций?

3 Solutions collect form web for “Динамическое построение фильтров в SQLAlchemy”

Если это кому-то полезно, вот что я сделал:

 from flask import request class Parser(object): sep = ';' # ... def filter_query(self, query): model_class = self._get_model_class(query) # returns the query's Model raw_filters = request.args.getlist('filter') for raw in raw_filters: try: key, op, value = raw.split(self.sep, 3) except ValueError: raise APIError(400, 'Invalid filter: %s' % raw) column = getattr(model_class, key, None) if not column: raise APIError(400, 'Invalid filter column: %s' % key) if op == 'in': filt = column.in_(value.split(',')) else: try: attr = filter( lambda e: hasattr(column, e % op), ['%s', '%s_', '__%s__'] )[0] % op except IndexError: raise APIError(400, 'Invalid filter operator: %s' % op) if value == 'null': value = None filt = getattr(column, attr)(value) query = query.filter(filt) return query 

Это охватывает все компараторы столбцов SQLAlchemy:

  • eq для ==
  • lt для <
  • ge для >=
  • in для in_
  • like like
  • и т.п.

Полный список с их соответствующими именами можно найти здесь .

Один полезный трюк при создании нескольких фильтров выражений:

 filter_group = list(Column.in_('a','b'),Column.like('%a')) query = query.filter(and_(*filter_group)) 

Использование этого подхода позволит вам комбинировать выражения с и / или логикой. Также это позволит вам избежать вызовов рекурсии, как в вашем ответе.

Вы можете использовать sqlalchemy-elasticquery для создания динамических фильтров с использованием SQLAlchemy.

 ?filters={ "age" : 3 } 
  • Как получить объект Flask-SQLAlchemy для загрузки дочерних элементов отношений для шаблона Jinja?
  • Как переопределить имя столбца в sqlalchemy с использованием рефлексивного и описательного синтаксиса
  • как сохранить двоичный файл, полученный Flask в postgres
  • Предельное переполнение лимита Sql Alchemy QueuePool
  • Не создавать родительский объект в сеансе session.dirty before_flush eventener
  • Загрузчик SQL Alchemy Relationship оставляет блокировку на столе?
  • Как использовать bindparam () в пользовательском выражении Compiled?
  • Заменить / удалить поле с помощью sqlalchemy
  • Python - лучший язык программирования в мире.