该用 connection.cursor() 时:执行 insert/update/delete/ddl、批量操作、复杂查询(如 with)、需 returning 或手动事务控制;该用 raw() 时:仅 select,且结果字段名与模型字段严格匹配、含主键、可映射为模型实例。

什么时候该用 connection.cursor 而不是 raw()
直接执行原生 SQL 的核心分两类:需要写入/DDL/复杂事务控制,或只需读取且结果能映射到已有模型。前者必须用 connection.cursor;后者可选 raw(),但前提是 SQL 返回字段名和模型字段严格匹配,否则 raw() 会静默失败或抛 FieldError。
常见误用是拿 raw() 去查聚合结果(比如 SELECT COUNT(*))——它只接受带主键的行数据,返回标量会直接报错 AttributeError: 'RawQuerySet' object has no attribute 'first'。
-
connection.cursor:适合 INSERT/UPDATE/DELETE、CREATE TABLE、带WITH子句的复杂查询、需手动管理事务的场景 -
raw():仅限 SELECT,且结果集列名必须与目标模型字段一致(包括大小写),不支持 JOIN 后字段重命名(如user.name AS username会被忽略) - 二者都不自动做 SQL 注入防护——参数必须用占位符
%s(PostgreSQL/MySQL)或?(SQLite),绝不能用 f-string 或str.format()
用 connection.cursor 安全执行带参数的 UPDATE
手动游标最常踩的坑是忘记关闭、没处理异常导致连接泄漏,以及参数传错类型(比如把字符串当整数传进 %s 占位符)。
正确写法必须包裹在 try/finally 或用上下文管理器,并显式调用 cursor.close():
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
from django.db import connection <p>def update_user_status(user_id, new_status): with connection.cursor() as cursor: cursor.execute( "UPDATE auth_user SET is_active = %s WHERE id = %s", [new_status, user_id] # 参数必须是 list 或 tuple ) return cursor.rowcount # 影响行数,非返回值</p>
- 不要用
cursor.fetchall()执行 UPDATE —— 它返回空列表,但容易让人误以为有数据 - PostgreSQL 用户注意:
%s是通用占位符,不是字符串拼接,不用加引号 - 如果 SQL 中有子查询或 CTE,确保外层 SELECT 的字段顺序和 Python 解包逻辑一致(比如
cursor.fetchone()返回元组)
raw() 查询失败的三个典型原因
raw() 看似简单,实际运行时经常无声失败。最常见的是字段名不匹配、缺少主键、或用了不支持的语法。
- 模型定义了
id字段,但 SQL 写成SELECT user_id AS id—— Django 不识别别名,会报KeyError: 'id' - SQL 包含
GROUP BY或聚合函数(COUNT,SUM),导致结果无主键字段,raw()直接拒绝构造模型实例 - 使用了数据库特有语法(如 MySQL 的
GROUP_CONCAT或 PostgreSQL 的json_agg),而对应字段在模型中是普通CharField,类型不兼容会引发TypeError
调试建议:先用 connection.cursor 执行同一 SQL,打印 cursor.description 确认列名和类型,再比对模型字段定义。
事务边界和连接复用的实际影响
Django 默认每个 HTTP 请求开启一个数据库连接,但 connection.cursor 和 raw() 都共享这个连接。这意味着:手动执行的 SQL 会参与当前事务,而 raw() 查询默认不触发事务提交。
- 如果在视图里先调用
raw()查数据,再用connection.cursor执行 UPDATE,二者在同一个事务里——回滚会影响全部 - 长任务中反复调用
connection.cursor但没及时 close,可能耗尽连接池(尤其用 pgbouncer 或连接池中间件时) - 异步视图(ASGI)中不能用
connection—— 它不是线程安全的,会报DatabaseWrapper objects created in a thread can only be used in that same thread
多线程环境必须用 django.db.connections['default'] 显式获取连接,并确认线程隔离设置。生产环境遇到“database is locked”或“too many clients”时,优先检查未关闭的游标。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










