20

假设我有一个这样的模型:

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()

要获取去南加州大学的用户列表,这是我的查询:

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

要获取来自纽约的用户列表以及前往南加州大学的用户列表,这是我的查询:

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 表达式,但是如何根据一个变量将几个链接在一起?

希望这更有意义。

谢谢!

4

3 回答 3

57

SQLAlchemyfilter_by采用关键字参数:

filter_by(**kwargs)

换句话说,该函数将允许您给它任何关键字参数。这就是为什么您可以在代码中使用任何您想要的关键字的原因:SQLAlchemy 基本上将参数视为值字典。有关关键字参数的更多信息,请参阅Python 教程

这样一来,SQLAlchemy 的开发人员就可以以字典形式接收任意一堆关键字参数。但你要求的是相反的:你可以任意一堆关键字参数传递给一个函数吗?

事实证明,在 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')
于 2013-10-21T23:12:13.897 回答
2

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)

(如果您使用此样板保存模​​块,则免费获取上述代码段)

于 2015-01-02T12:44:30.893 回答
0

正如@opyate 指出的那样,filter_by(**request.args)如果您有非模型查询参数(例如page分页),则效果不佳,也可以使用以下替代方法:

假设page采用 的形式request.args.get(),则:

def get_list(**filters):
    page = None
    if 'page' in filters:
        page = filters.pop('limit')
    items = Price.query.filter_by(**filters)
    if page is not None:
        items = items.paginate(per_page=int(page)).items
    else:
        items = items.all()
    return {
        "items": items
    }

然后是get函数

def get(self):
    hometown = request.args.get('hometown')
    university = request.args.get('university')
    page = request.args.get('page')
    return get_list(**request.args)

我已经尝试在我的烧瓶应用程序上实现这个,它运行顺利。

当然,可能的一个缺点是,如果有多个这样page的值不是模型的一部分,那么它们中的每一个都必须在 中单独定义get_list,但这可以通过列表理解来完成

于 2020-05-21T10:02:44.693 回答