async for 遍历 queryset 必须用 afilter() 而非 all(),因 all() 返回不可 await 的 queryset;流式导出大 excel 应用 streaminghttpresponse 包裹异步生成器,边查边写,避免内存溢出和同步 i/o 阻塞。

async for 遇到 QuerySet 时必须用 afilter() 而不是 all()
直接 await MyModel.objects.all() 会报错,因为 all() 本身不返回可 await 对象;它只是个 QuerySet 构造器,没触发查询。真正要流式遍历,得用异步 ORM 方法 + async for 组合。
常见错误现象:写 async for obj in MyModel.objects.all(): → 报 TypeError: 'QuerySet' object is not async iterable。
- ✅ 正确写法:
async for obj in MyModel.objects.afilter():(哪怕不加条件也得用afilter()) - ⚠️ 注意:
afilter()返回的是AsyncQuerySet,它实现了__aiter__,才能被async for消费 - ❌ 不要用
list()或await .alast()等操作提前耗尽迭代器,否则后续async for拿不到数据
流式导出大 Excel 时别在内存里攒整张表
用 openpyxl 写大文件时,如果先构建完整 Workbook 再 HttpResponse,很容易 OOM。正确做法是边查边写,靠异步生成器把每行数据“推”进响应流。
关键点在于:不能等所有数据库行查完再开始写 Excel,而要让数据库查询、单元格写入、HTTP 响应三者重叠执行。
- 用
StreamingHttpResponse包裹一个异步生成器,响应头设Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - 生成器内部用
async for row in MyModel.objects.afilter().values_list('col1', 'col2')拉数据 - 每拿到一批行(比如 1000 条),就用
openpyxl的append()写入工作表,并调用save()到BytesIO缓冲区,yield 出 chunk - 避免用
save()全量写入磁盘文件,那会阻塞事件循环
异步生成器里混用同步 I/O 会卡死整个事件循环
比如在 async for 循环里调 time.sleep(1) 或 os.path.getsize(),表面看只是“小操作”,实际会让所有并发请求一起等——因为这是同步阻塞调用,事件循环无法切换。
典型踩坑场景:想给流式响应加人工延迟模拟慢速推送,结果发现所有 WebSocket 连接都卡住。
- ✅ 替代方案:用
await asyncio.sleep(1) - ✅ 文件元信息查取:改用
aiofiles.os.stat()(需安装aiofiles) - ✅ 日志记录:别用
print()或logging.info(),它们底层是同步 I/O;换asyncio.to_thread(logging.info, ...)或用支持异步的日志库 - ⚠️ 特别注意:Django 的
settings.DEBUG开启时,模板渲染、SQL 日志等全是同步行为,会显著拖慢异步流
前端接收流式响应必须用 fetch + ReadableStream,不能用 axios
Django 后端用 StreamingHttpResponse 推送 chunk,但前端若用 axios.get(),默认会等整个响应结束才触发 then(),根本收不到中间数据。
只有原生 fetch() 支持逐块读取,且需手动处理 response.body.getReader() 和 decoder.decode()。
- ✅ 正确流程:
const response = await fetch('/export/'); const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; /* 处理 value(Uint8Array)*/ } - ❌
axios目前(v1.7+)仍不支持流式响应解析,response.data是完整 buffer - ⚠️ 中文文件名下载:
Content-Disposition头里的 filename* 参数必须 UTF-8 编码并加UTF-8''前缀,否则 Safari 会乱码
异步生成器的威力不在“快”,而在“不卡”。只要有一处同步 I/O 漏网,整个流式链路就退化成串行阻塞——这种问题往往在线上压测时才暴露,且日志里毫无异常。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











