如何将自定义语法的查询字符串解析为Django ORM的Q查询过滤条件?
解决方案
工具适配情况
没有和你给出的这类自定义DSL查询语法完全匹配的开箱即用工具,但完全不需要从零手写完整的词法、语法解析逻辑,借助Python生态已有的成熟解析库就能快速实现转换需求。
可选实现方案
- 生产环境优先选择
pyparsing轻量级解析库实现,只需要定义少量语法规则即可完成表达式到Django Q对象的转换,天然规避代码注入风险,也方便后续扩展更多查询规则(比如大于、小于、模糊匹配等)。 - 如果你的查询语法不会再扩展,且所有查询字符串都是内部生成、完全可控(无外部用户输入),也可以用字符串替换配合
eval快速实现,代码量极低,但严格禁止在有外部输入的场景使用该方案。
pyparsing实现示例
import functools from pyparsing import * from django.db.models import Q from yourapp.models import People # 定义基础语法元素 identifier = Word(alphas, alphanums + "_") number = Word(nums).set_parse_action(lambda t: int(t[0])) string = QuotedString('"') operand = identifier | number | string expr = Forward() # 定义EQUAL、OR、AND规则 equal = Group("EQUAL" + "(" + identifier + "," + operand + ")").set_parse_action(lambda t: Q(**{t[1]: t[2]})) or_clause = Group("OR" + "(" + delimited_list(expr) + ")").set_parse_action(lambda t: functools.reduce(Q.__or__, t[1])) and_clause = Group("AND" + "(" + delimited_list(expr) + ")").set_parse_action(lambda t: functools.reduce(Q.__and__, t[1])) expr <<= (equal | or_clause | and_clause) # 测试转换 query1 = 'OR(EQUAL(id,111),EQUAL(id,222))' q_obj1 = expr.parse_string(query1, parse_all=True)[0] People.objects.filter(q_obj1) # 等价于 Q(id=111) | Q(id=222) query2 = 'AND(EQUAL(id,333),EQUAL(name,"John"))' q_obj2 = expr.parse_string(query2, parse_all=True)[0] People.objects.filter(q_obj2) # 等价于 Q(id=333) & Q(name="John")
注意事项
如果查询字符串来自外部用户输入,必须在解析层添加字段白名单校验,禁止用户传入敏感字段作为过滤条件,避免数据泄露风险。
内容的提问来源于stack exchange,提问作者Zito Relova
相关产品推荐
相关产品推荐

