如何在Django原生SQL的WHERE IN语句中传入列表参数?
Django原生SQL中UUID列表注入WHERE IN的正确姿势
问题背景
需要在Django原生SQL的WHERE ... IN (...)语句中注入UUID列表,示例UUID列表:
list_of_uuids = [ "<UUID_1>", "<UUID_2>" ]
期望生成的目标SQL:
SELECT * FROM some_model_table WHERE some_model_table.uuid IN ( "<UUID_1>", "<UUID_2>" )
直接使用raw()传递列表时,参数会被解析为SQL数组,触发语法错误:
django.db.utils.ProgrammingError: syntax error at or near "ARRAY": LINE X: WHERE uuid IN ARRAY['00123...
将数组包裹为(ARRAY[...])会因类型不匹配报错,且不能使用.format()拼接(存在SQL注入风险),同时实际查询逻辑复杂,无法通过ORM的filter(uuid__in=...)实现。
可行解决方案
方法1:手动构造参数占位符(通用所有数据库)
根据UUID列表的长度,生成对应数量的%s占位符,再将列表拆分为参数传递,全程保持参数化,无注入风险:
# 根据UUID数量生成占位符,比如2个UUID生成 "%s, %s" placeholders = ", ".join(["%s"] * len(list_of_uuids)) # 构造带占位符的SQL语句 sql = f"SELECT * FROM some_model_table WHERE some_model_table.uuid IN ({placeholders})" # 执行原生查询,直接传递UUID列表作为参数 query_set = SomeModel.objects.raw(sql, list_of_uuids)
方法2:PostgreSQL专属数组运算符
如果使用PostgreSQL数据库,可以用= ANY()替代IN,直接传递列表参数即可,数据库会自动解析数组:
sql = "SELECT * FROM some_model_table WHERE some_model_table.uuid = ANY(%s)" query_set = SomeModel.objects.raw(sql, [list_of_uuids])
最终生成的SQL为WHERE uuid = ANY(ARRAY['uuid1', 'uuid2']),PostgreSQL可直接识别,且保持参数化安全。
方法3:借助游标生成适配SQL
利用Django数据库游标的mogrify方法,自动生成符合数据库语法的参数化语句:
from django.db import connection with connection.cursor() as cursor: # 生成占位符 placeholders = ", ".join(["%s"] * len(list_of_uuids)) # mogrify会将参数转换为数据库兼容的格式 sql = cursor.mogrify( f"SELECT * FROM some_model_table WHERE some_model_table.uuid IN ({placeholders})", list_of_uuids ).decode() # 执行原生查询 query_set = SomeModel.objects.raw(sql)
这种方式能确保SQL格式完全适配当前数据库,同时避免注入风险。
内容的提问来源于stack exchange,提问作者hiham
相关产品推荐
相关产品推荐

