以关键字为变量的烧瓶 sqlalchemy 查询

Posted

技术标签:

【中文标题】以关键字为变量的烧瓶 sqlalchemy 查询【英文标题】:flask sqlalchemy query with keyword as variable 【发布时间】:2013-10-30 15:05:43 【问题描述】:

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

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

希望这更有意义。

谢谢!

【问题讨论】:

是的,我们可以帮助您构建该功能。当您尝试自己构建它时遇到了什么问题? 尝试将变量(例如:kv = 'hometown')分配为关键字 arg 时,出现此错误:nvalidRequestError: Entity '' has no property 'kv' 您能否将该代码显示为问题的一部分?实际上,您的问题似乎显示了您所知道的,而不是要求您解释您试图回答的内容。 知道了...现在添加更多。感谢您的帮助。 我相信您可以将单个字典(不是字典列表)直接传递给filter_by。所以dict = 'hometown': 'New York', 'university': 'USC'User.query.filter_by(dict).all()。但是,我对此并不积极。 【参考方案1】:

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

【讨论】:

宾果游戏!谢谢老兄-这正是我想要的。感谢您的帮助! 你如何通过这样做来实现“in_”? @Ricky 不幸的是,我有一段时间没有使用 SQLAlchemy,所以我不知道。你最好问一个新问题。【参考方案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)

(使用这个boilerplate-saving module可以免费获得上面的sn-p)

【讨论】:

这是否适用于嵌套属性?例如, product.category.name ?如果没有,有办法吗? 你的意思是一个类别在哪里是产品的外键?在那种情况下,不 :) 可能有办法做到这一点,是的。【参考方案3】:

正如@opyate 所指出的,如果您有非模型查询参数,例如page 用于分页,filter_by(**request.args) 不能很好地工作,也可以使用以下替代方法:

假设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 中单独定义,但可以这样做通过列表理解

【讨论】:

以上是关于以关键字为变量的烧瓶 sqlalchemy 查询的主要内容,如果未能解决你的问题,请参考以下文章

create_engine 中的烧瓶 SQLAlchemy KeyError 'False'

用于生产和开发的烧瓶 postgresql sqlalchemy 配置

烧瓶-sqlalchemy 或 sqlalchemy

使用来自 SQLAlchemy 对象的数据在烧瓶中预填充 WTforms

烧瓶躁动无法构造查询

SQLAlchemy 查询返回无