39

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のドキュメントを検索したところ、2つの潜在的なリードが見つかりましたが、どちらも満足のいくものではなかったようです。

  • using Column.likeColumn.in_...:これらの演算子は列で直接使用できるため、簡単に使用できますgetattrが、一部が欠落しています(、、==など>)。

  • Column.op:egを使用しますが、これはすべてのオペレーター(つまり)Cat.name.op('=')('Hobbes')で機能するとは限りません。in

機能なしでこれを行うためのクリーンな方法はありlambdaますか?

4

5 に答える 5

56

これが誰かに役立つ場合に備えて、私がやったことは次のとおりです。

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

対応する名前の完全なリストは、ここにあります。

于 2013-02-14T13:51:08.830 に答える
23

複数式フィルターを作成する際の1つの便利なトリック:

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

このアプローチを使用すると、式をロジックと組み合わせることができます。また、これにより、回答のように再帰呼び出しを回避できます。

于 2013-02-15T03:23:28.433 に答える
1

SQLAlchemy を使用して動的フィルターを構築するには、 sqlalchemy-elasticqueryを使用できます。

?filters={ "age" : 3 }
于 2015-03-17T18:57:35.973 に答える