Django SQL查询中转义%报错:列表索引越界
Django原生SQL查询列表索引越界问题排查
场景还原
在pgAdmin中可正常执行的SQL查询:
SELECT <columns> FROM <tables> WHERE date_visited >= '2023-05-26 07:05:00'::timestamp AND date_visited <= '2023-05-26 07:07:00'::timestamp AND url LIKE '%/mymodule/api/myurl/%';
为了在Django REST接口中调用该查询,编写了如下代码,运行后出现列表索引越界错误:
with connection.cursor() as cursor: cursor.execute(''' SELECT <columns> FROM <tables> WHERE date_visited >= '%s'::timestamp AND date_visited <= '%s'::timestamp AND url LIKE '%%%s%%'; ''', [from_date, to_date, url])
问题根源
问题确实出在'%%%s%%'的写法上:
Django的cursor.execute采用参数化查询,这里的%s是数据库占位符,但你把它和Python的字符串转义%%混在了一起。Python会先解析这个字符串:%%%s%%会被转成%s%(第一个%%转为单个%,中间%s保留,最后%%转为单个%),导致实际传入的参数数量和SQL里的占位符数量不匹配,触发索引越界错误。
正确解决方案
方案1:在参数中拼接通配符
把%直接加到传入的url参数前后,SQL里只用%s作为占位符(注意去掉占位符的单引号,参数化查询会自动处理字符串引号):
# 给url前后加上通配符 target_url = f'%{url}%' with connection.cursor() as cursor: cursor.execute(''' SELECT <columns> FROM <tables> WHERE date_visited >= %s::timestamp AND date_visited <= %s::timestamp AND url LIKE %s; ''', [from_date, to_date, target_url])
方案2:用PostgreSQL字符串连接符拼接通配符
如果想在SQL里写通配符,用PostgreSQL的||连接符把%和占位符拼接,避免Python转义冲突:
with connection.cursor() as cursor: cursor.execute(''' SELECT <columns> FROM <tables> WHERE date_visited >= %s::timestamp AND date_visited <= %s::timestamp AND url LIKE '%' || %s || '%'; ''', [from_date, to_date, url])
额外提示
- 不要混用Python字符串格式化和数据库参数化占位符,既容易出错,还会带来SQL注入风险。
- 如果
from_date和to_date是Python的datetime对象,可以直接传入,不需要手动转成字符串加::timestamp,PostgreSQL会自动处理类型转换。
内容的提问来源于stack exchange,提问作者MsA
相关产品推荐
相关产品推荐

