在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
相关产品推荐
相关产品推荐

