如何将原生查询集转换为Django QuerySet?使用RawSQL报缺少params参数错误
报错原因
- 直接触发报错的逻辑是:
RawSQL的构造方法必须接收两个参数——原生SQL字符串、参数列表params,你没有传递params参数,仅靠字符串格式化把动态值塞进了SQL字符串,不符合API要求 - 隐藏的逻辑问题:你写的子查询返回了3个字段(
test1.id、test1.name、test1.start_time),但id__in的过滤规则要求子查询只能返回单列的id值,就算修复了构造函数的报错,查询执行时也会被数据库抛出“子查询返回列数不匹配”的错误
可直接运行的修正代码
from django.db.models.expressions import RawSQL from xyz.models import Test1 value = '1.2.3.4' # 1. 把动态值占位符替换为Django统一的%s,不再手动拼接 # 2. 子查询仅保留需要返回的id列,适配id__in的匹配要求 raw_id_subquery = RawSQL( """ SELECT DISTINCT ON (test1.start_time, test1.id) test1.id FROM test1 WHERE EXISTS ( SELECT * FROM test2 JOIN test3 ON test2.test3_id = test3.id AND test3.value = %s JOIN test4 ON test2.test4_id = test4.id AND test4.test1_id = test1.id ) ORDER BY test1.start_time DESC """, # 把动态值放到params参数中,由Django自动完成转义 params=[value] ) # 得到标准Django QuerySet,可直接用于后续的所有ORM操作 queryset = Test1.objects.filter(id__in=raw_id_subquery)
关键注意点
- 绝对不要手动拼接SQL动态值:这种写法除了触发API报错,还会存在极高的SQL注入风险,通过
params传值的安全处理逻辑和Django原生ORM完全一致 - 子查询的返回列数必须匹配外层过滤要求:所有使用
__in做字段匹配的场景,对应的子查询必须仅返回单列值,多列返回无法被数据库正常执行 - 上述方式生成的QuerySet是完全标准的Django查询集,后续的过滤、排序、注解、分页、序列化等操作,和你直接用原生ORM编写的查询没有任何行为差异
内容的提问来源于stack exchange,提问作者Sandesh Veerapur
相关产品推荐
相关产品推荐

