You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django中对自定义原生SQL查询执行过滤搜索且防SQL注入的方案有哪些?

Django 原生提供的数据库接口已经完全满足你的需求,无需引入第三方依赖,核心是通过官方提供的参数绑定机制避免SQL注入,同时可灵活支持你需要的所有运算符操作。


基础实现方案

所有动态输入的数值都不要直接拼接进SQL字符串,统一通过Django数据库接口的params参数传递,数据库驱动会自动完成转义,从根源避免SQL注入风险。

from django.db import connection

def get_customers(name_keyword, age_threshold, status_list):
    with connection.cursor() as cursor:
        # SQL 语句只留占位符,不写入任何动态输入内容
        sql = """
        SELECT * 
        FROM customers 
        WHERE customers.name LIKE %s 
          AND age < %s 
          AND status IN %s
        """
        # 所有动态值统一放入params列表,自动处理转义
        params = [
            f"%{name_keyword}%",  # 模糊匹配的%直接在参数侧处理即可
            age_threshold,
            tuple(status_list)  # IN操作的入参直接转成元组传入即可
        ]
        cursor.execute(sql, params)
        # 结果转换为字典格式返回
        columns = [col[0] for col in cursor.description]
        return [dict(zip(columns, row)) for row in cursor.fetchall()]

分场景实现说明

  • 模糊匹配(LIKE):不要把%写到SQL语句的占位符里,直接在参数侧给输入值前后拼接%即可,转义逻辑会自动处理关键词里的特殊字符
  • 动态比较运算符(<、>、=等):运算符属于SQL语法部分,不能放到参数里传递,如果需要动态切换运算符,提前做白名单校验即可,示例如下:
    # 允许使用的运算符白名单
    ALLOWED_OPS = {"gt": ">", "lt": "<", "eq": "=", "gte": ">=", "lte": "<="}
    # 取用户传入的操作符类型,匹配白名单,未匹配到则默认用等号
    op = ALLOWED_OPS.get(user_input_op, "=")
    # 拼接SQL时用校验后的运算符,不存在注入风险
    sql = f"SELECT * FROM customers WHERE age {op} %s"
    
  • IN操作:直接把列表类型的入参转成元组传入params即可,Django数据库驱动会自动生成符合SQL规范的('val1','val2',...)格式,同时自动转义列表内的每个值。

进阶:封装类ORM的过滤语法

如果你想要和Django ORM类似的field__xx调用体验,可以自己封装轻量的过滤构造逻辑,不用依赖额外包:

# 提前定义过滤规则:操作类型对应SQL运算符、参数处理函数
FILTER_RULES = {
    "contains": ("LIKE", lambda val: f"%{val}%"),
    "gt": (">", lambda val: val),
    "lt": ("<", lambda val: val),
    "eq": ("=", lambda val: val),
    "in": ("IN", lambda val: tuple(val)),
}

def build_where_condition(filters: dict):
    where_parts = []
    params = []
    for filter_key, filter_val in filters.items():
        field, op_type = filter_key.split("__", 1)
        # 可额外增加字段白名单校验,避免用户查询敏感字段
        if op_type not in FILTER_RULES:
            continue
        op, val_handler = FILTER_RULES[op_type]
        where_parts.append(f"{field} {op} %s")
        params.append(val_handler(filter_val))
    where_clause = " AND ".join(where_parts) if where_parts else "1=1"
    return where_clause, params

# 调用方式和Django ORM几乎完全一致
filters = {
    "name__contains": "张三",
    "age__lt": 30,
    "status__in": ["active", "pending"]
}
where_sql, params = build_where_condition(filters)
full_sql = f"SELECT * FROM customers WHERE {where_sql}"
# 后续执行逻辑和基础方案一致,传入params即可

内容的提问来源于stack exchange,提问作者jay gajjar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 12:36:06