django-import-export直接处理百万行Excel会OOM是默认行为,因其将全量数据读入内存;应改用openpyxl read_only=True流式读取+bulk_create分批写入,并禁用验证与信号以规避性能瓶颈。

用 django-import-export 直接处理百万行 Excel 会 OOM
内存爆掉是默认行为——它把整个 Excel 文件读进 Python 对象再逐行导入,100 万行 × 10 列的 .xlsx 在 Pandas 或 openpyxl 后端下常吃掉 2–4GB 内存,Django 进程直接被系统 kill。这不是配置问题,是设计使然。
实操建议:
- 禁用
import_export的默认ModelResource导入流程,改用流式解析 + 批量写入 - 用
openpyxl的read_only=True模式打开文件,配合iter_rows()迭代器逐行读,不缓存全量数据 - 每 5000 行调用一次
MyModel.objects.bulk_create(...),避免单次 SQL 过长或事务锁表太久 - 导出时绝不走
HttpResponse+ 全量内存构建,改用StreamingHttpResponse+openpyxl.writer.excel.write_data分块写入
openpyxl 流式读取时如何跳过表头、识别空行、处理日期格式
Excel 常见脏数据会让 iter_rows() 返回 None 或空元组,且日期单元格默认是 datetime 对象而非字符串,直接 str() 可能报错。
实操建议:
- 用
worksheet.iter_rows(min_row=2, values_only=True)跳过首行表头(假设第 1 行是字段名) - 对每一行做
if not any(cell is not None and cell != "" for cell in row): continue过滤纯空行 - 检测单元格类型:
if isinstance(cell, datetime): value = cell.strftime('%Y-%m-%d'),避免strftime在None上崩溃 - 数值型单元格可能带小数(如
123.0),用int(cell) if isinstance(cell, float) and cell.is_integer() else cell清洗
Django 中批量导入时如何绕过模型验证和信号触发
百万级导入时,full_clean() 和 pre_save/post_save 信号会成为性能瓶颈,尤其当信号里有网络请求或复杂逻辑时。
实操建议:
- 导入阶段显式关闭验证:
MyModel.objects.bulk_create(obj_list, ignore_conflicts=True)(注意:PostgreSQL 支持ignore_conflicts,MySQL 需用on_conflict替代) - 临时禁用信号:
from django.dispatch import receiver; from myapp.signals import my_signal; my_signal.disconnect(receiver_func),导入完再 reconnect - 若必须校验,改在导入前用 Pandas 或生成器做轻量校验(如邮箱格式、必填字段非空),不走 Django ORM 层
- 外键字段不要传字符串,提前用
dict(zip(ids, qs))构建 ID → 实例映射,避免循环中重复查库
导出百万行 Excel 为什么不能用 HttpResponse?StreamingHttpResponse 怎么配 openpyxl
HttpResponse 会把整个 Workbook 写进内存再发出去,100 万行导出常卡死或超时;而 StreamingHttpResponse 要求响应体是可迭代对象,但 openpyxl 默认不支持流式写入。
实操建议:
- 不用
workbook.save(),改用workbook.write_data()获取底层 XML 数据块 - 自定义一个生成器函数,每次 yield 一个
BytesIO中的 chunk(需手动拼接[Content_Types].xml、xl/workbook.xml等基础结构) - 更稳妥的做法:导出为
.csv(用StreamingHttpResponse+ 生成器逐行 yield 字符串),前端用 SheetJS 转 Excel;或后端用xlsxwriter(它原生支持 streaming 写入)替代openpyxl - 务必设置
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet和Content-Disposition,否则浏览器无法识别为 Excel
真正卡住的往往不是代码怎么写,而是没意识到 Excel 导入导出本质是 I/O 密集型任务,得按流式思维重设计——文件不全读、数据库不单条插、响应不全缓存。openpyxl 的 read_only 和 xlsxwriter 的 add_worksheet 是两个关键支点,选错就只能降级到分页导出或者换 Parquet。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











