sqlalchemy 如何现实动态查询条件以及动态字段查询

一个项目中需要会有多个字段以多个查询条件,
并且这些条件是不固定的

DEMO

    # 数据库模型
    class Customs(db.Model):
            __tablename__ = 'customs'
            id = db.Column(db.Integer, primary_key=True)
            name = db.Column(db.String(64))
             phone = db.Column(db.String(64))


   # 视图中使用
   @app.route('/table/', methods=['GET', 'POST'])
   def customs():
   # 接收查询的字段名称 可能是name/phone等变量
   field = request.args.get('field')
    
   # 假设 field 接收到的值是 'name' , 正常的查询是这样的
   # customs = Customs.name.query.all()
   # 我想实现根据接收到的变量进行查询 
   # 类似这种方式

   if field:
          customs = Customs[field].query.all()
    
   # 这样写会报错
   # >>>TypeError: '_BoundDeclarativeMeta' object is not subscriptable

一番查询之后,找到这样一个解决方案
这个方案需要把所有可能的条件全部if else枚举出来 再传给filter
并不是理想的解决方案

开发中,我们经常会碰到根据传入的参数构造查询条件进行查询。比如

  • 如果接收到非 0 的 cat_id ,需要限制 cat_id 等于 0
  • 如果接收到非 0 的 price,需要限制 price 等于传入的 price
  • 如果接收到非 0 的 min_price ,需要限制 price 大于等于 min_price
  • 如果接收到非 0 的 max_price ,需要限制 price 小于等于 max_price

我们就可以编写类似的代码

# 请求参数,这里只是占位,实际由用户提交的请求决定
params = {'cat_id': 1}

conditions = []
if params.get('cat_id', 0):
    conditions.append(Book.cat_id == params['cat_id'])
if params.get('price', 0):
    conditions.append(Book.price == params['price'])
if params.get('min_price', 0):
    conditions.append(Book.price >= params['min_price'])
if params.get('max_price', 0):
    conditions.append(Book.price <= params['max_price'])
books = session.query(Book).filter(*conditions).all()

print([v.to_dict() for v in books])
复制代码

结果

[{'id': 1, 'cat_id': 1, 'name': '生死疲劳',
  'price': Decimal('40.40')},
 {'id': 2, 'cat_id': 1, 'name': '皮囊',
  'price': Decimal('31.80')}]
复制代码

OR 查询类似,将列表解包传给 or_() 即可。

这个帖子还提有另一种解决方案

当然,如果都是等值查询的话,比如只有这两种情况

  • 如果接收到非 0 的 cat_id ,需要限制 cat_id 等于 0
  • 如果接收到非 0 的 price,需要限制 price 等于传入的 price

可以使用字典的解包给 filter_by() 传参

# 请求参数,这里只是占位,实际由用户提交的请求决定
params = {'price': 31.1}

condition_dict = {}
if params.get('cat_id', 0):
    condition_dict['cat_id'] = params['cat_id']
if params.get('price', 0):
    condition_dict['price'] = params['price']
books = session.query(Book) \
    .filter_by(**condition_dict) \
    .all()

print([v.to_dict() for v in books])
复制代码

结果

[{'id': 6, 'cat_id': 3, 'name': '时间简史',
  'price': Decimal('31.10')}]

如帖子所说,这种方式只能解决等值(=)的情况
而实际业务中有=、>=、<=、!= 、%like%等情况

如何现实这种动态字段查询?

假设 field 接收到的值是 ‘name’ , 正常的查询是这样的

customs = Customs.name.query.all()

你是不是搞错了?如果你想查询 Customs 表的所有记录,那就是:

customs = Customs.query.all()

如果你想查询指定字段值的数据,那不仅需要字段名称,还需要对应的字段值才行,比如查询 name 为 jack 的记录:

customs = Customs.query.filter_by(name='jack').all()

如果你想动态查询某个字段(字符串形式的字段名)和对应的值,可以用内置函数 getattr,比如:

field = request.args.get('field')
value = request.args.get('value')

customs = Customs.query.filter(getattr(Customs, field) == value).all()
1 个赞

感谢,我试一下

感谢大佬,这样确实是可以的。

不客气 : )