根本原因是csv缺少bom头导致excel用gbk解析utf-8;应手动添加codecs.bom_utf8,用values_list()安全导出,大表需iterator分批+streaminghttpresponse流式响应,异步任务处理耗时导出,并由csv.writer自动转义特殊字符。

导出 CSV 时中文乱码或 Excel 打开是乱码
根本原因不是 Django,而是 CSV 文件缺少 BOM 头,Excel 默认用 ANSI(如 GBK)解析 UTF-8 内容。Python 的 csv.writer 默认不写 BOM,直接写 UTF-8 字节流,Excel 就懵了。
- 解决方法:用
io.StringIO+codecs.BOM_UTF8手动加 BOM,或更稳妥地用response.write(codecs.BOM_UTF8)开头 - 别用
open(..., 'w', encoding='utf-8')直接写文件再读取——Django 响应要流式输出,且文件句柄容易在多线程下出问题 - 如果用
StreamingHttpResponse,BOM 必须作为第一块数据发出,否则无效
Django Model 数据导出到 CSV 的最小安全写法
别手写字段拼接,也别用 model_to_dict 硬转——它不处理外键、日期格式、空值和自定义 property。应该用 QuerySet 的 values_list() 或 values() 配合明确字段名。
- 推荐用
qs.values_list('id', 'name', 'created_at', 'status'),字段顺序可控,性能好,不触发 model 实例化 - 日期字段会自动转成字符串(ISO 格式),但若需自定义格式(如“2024-03-15 14:22”),得在
values()中用F+Cast或数据库函数,或导出前用 Python 格式化(注意时区) - 外键字段写
'category__name'可跨表取值,但避免深度嵌套('author__profile__phone'),会显著拖慢查询
导出大表卡死或内存爆掉怎么办
QuerySet 默认惰性求值,但 list(qs) 或 qs.values_list() 全量加载时,10 万条记录可能吃掉几百 MB 内存。尤其在 Gunicorn/uWSGI 下,worker 进程容易被 OOM kill。
- 必须用
iterator(chunk_size=2000)分批读取,配合StreamingHttpResponse流式写入 - 别在循环里反复调用
response.write()单行——改用csv.writer写入io.StringIO缓冲区,满 100 行 flush 一次 - 数据库层面加
.only('id', 'name')减少字段加载,避免TextField或 JSON 字段拖慢 I/O
用户点击就导出,但导出失败没提示
HTTP 是无状态的,导出过程耗时、可能失败(DB 超时、磁盘满、内存不足),但浏览器只等响应头,一旦超时就显示“网络错误”,用户完全不知道发生了什么。
- 简单场景:用
try/except捕获OperationalError、MemoryError,返回HttpResponseServerError并带简短错误信息(如“数据量过大,请联系管理员”) - 重要业务(如财务报表):必须走异步任务(Celery/RQ),前端轮询状态,后端存临时文件或 URL,导出完成再重定向下载
- 千万别把
print()或日志埋点当反馈——用户看不见,运维也难定位是哪次请求挂了
最常被跳过的细节是:没验证字段名是否含逗号、换行符、双引号。CSV 标准要求这些字符必须用双引号包裹并转义,csv.writer 会自动处理,但自己拼字符串就全崩了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











