web-dev-qa-db-ja.com

flask変数としてキーワードを使用したsqlalchemyクエリ

私がこのようなモデルを持っているとしましょう:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    hometown = db.Column(db.String(140))
    university = db.Column(db.String(140))

ニューヨークからユーザーのリストを取得するには、これが私のクエリです。

User.query.filter_by(hometown='New York').all()

USCにアクセスするユーザーのリストを取得するには、次のようにします。

User.query.filter_by(university='USC').all()

そして、ニューヨークから、そしてUSCに行くユーザーのリストを取得するために、これは私の質問です:

User.query.filter_by(hometown='New York').filter_by(university='USC').all()

ここで、変数の値に基づいてこれらのクエリを動的に生成したいと思います。

たとえば、私の変数は次のようになります。

    {'hometown': 'New York'}

またはこのように:

    {'university': 'USC'}

...またはこのように:

    [{'hometown': 'New York'}, {'university': 'USC'}]

辞書(または辞書のリスト)を入力として受け取り、正しいsqlalchemyクエリを動的に構築する関数の作成を手伝ってもらえますか?

キーワードに変数を使用しようとすると、次のエラーが発生します。

key = 'university'
User.query.filter_by(key='USC').all()

InvalidRequestError: Entity '<class 'User'>' has no property 'key'

次に、複数のfilter_by式を動的にチェーンする方法がわかりません。

Filter_by式を明示的に呼び出すことはできますが、変数に基づいて複数をチェーンするにはどうすればよいですか?

これがもっと理にかなっていることを願っています。

ありがとう!

15
SeanPlusPlus

SQLAlchemyの filter_by はキーワード引数を取ります:

filter_by(** kwargs)

つまり、この関数を使用すると、任意のキーワードパラメータを指定できます。これが、コードで必要な任意のキーワードを使用できる理由です。SQLAlchemyは、基本的に引数を値の辞書と見なします。キーワード引数の詳細については、 Pythonチュートリアル を参照してください。

これにより、SQLAlchemyの開発者は辞書形式で任意のキーワード引数を受信することができます。しかし、あなたは反対のことを求めています:関数に任意のキーワード引数の束をpassできますか?

Pythonでは、 unpacking という機能を使用できます。引数の辞書を作成し、次のように**が前に付いた関数に渡します。

kwargs = {'hometown': 'New York', 'university' : 'USC'}
User.query.filter_by(**kwargs)
# This above line is equivalent to saying...
User.query.filter_by(hometown='New York', university='USC')
38
Mark Hildreth

filter_by(**request.args)は、ページ付け用のpageのようなモデル以外のクエリパラメータがある場合はうまく機能しません。そうでない場合、次のようなエラーが発生します。

_InvalidRequestError: Entity '<class 'flask_sqlalchemy.MyModelSerializable'>' has no property 'page'
_

モデルにないクエリパラメータを無視する次のようなものを使用します。

_    builder = MyModel.query
    for key in request.args:
        if hasattr(MyModel, key):
            vals = request.args.getlist(key) # one or many
            builder = builder.filter(getattr(MyModel, key).in_(vals))
    if not 'page' in request.args:
        resources = builder.all()
    else:
        resources = builder.paginate(
            int(request.args['page'])).items
_

validという列を持つモデルを考えると、次のように機能します。

_curl -XGET "http://0.0.0.0/mymodel_endpoint?page=1&valid=2&invalid=whatever&valid=1"
_

invalidは無視され、pageはページ付けに使用できます。何よりも、次のSQLが生成されます。WHERE mymodel.valid in (1,2)

(これを使用する場合は、上記のスニペットを無料で入手してください boilerplate-saving module

1
opyate