使用关键字作为变量的flask sqlalchemy查询

时间:2013-10-21 22:40:13

标签: python sql sqlalchemy flask 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()

要获得纽约用户列表以及前往南加州大学的用户,这是我的疑问:

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

希望这更有意义。

谢谢!

3 个答案:

答案 0 :(得分:36)

SQLAlchemy的filter_by接受关键字参数:

  

filter_by(** kwargs)

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

这样,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')

答案 1 :(得分:1)

如果您有非模型查询参数(例如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

,请免费获取上述代码段

答案 2 :(得分: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)

我已经尝试在我的flask应用程序中实现此功能,并且运行顺利。

当然,可能存在的一个缺点是,如果存在page等多个不属于模型的值,则每个值都必须在get_list中单独定义,但是可以通过列表理解来完成