only()只加载指定字段,defer()排除指定字段;二者互斥,后调用者覆盖前者;only()适用于固定少字段读取,漏外键或非空字段可能导致n+1或save失败;defer()对大字段延迟需谨慎,不支持多对多字段,values后调用无效。

only() 和 defer() 的核心区别在哪?
only() 明确指定只加载哪些字段,其余全部忽略;defer() 则相反,明确排除某些字段,其余全查。两者都作用于 QuerySet,且仅影响数据库 SELECT 子句中的字段列表,不改变模型实例的字段定义。
-
only()后访问未包含的字段会触发额外的 SQL 查询(lazy loading),除非该字段是外键或已缓存 -
defer()后访问被延迟的字段也会触发单次反向查询,但 Django 会批量加载所有被 defer 的字段(只要它们属于同一模型) - 二者不能混用:后调用的会覆盖前一个,比如
qs.only('name').defer('email')等价于qs.defer('email')(only()被丢弃)
什么时候用 only() 更安全?
适合字段数量少、业务逻辑明确只读固定几列的场景,比如后台列表页只展示 title、status、created_at。
- 避免在
only()中漏掉外键字段(如author_id),否则访问obj.author会触发 N+1 查询 - 如果后续要调用
save(),必须确保only()包含所有非空约束字段(如id、required字段),否则可能抛出ValueError: Cannot force an update in save() with no primary key - 对
DateTimeField或AutoField等默认有值的字段,漏掉不一定报错,但修改后save(update_fields=...)可能意外覆盖为 None
defer() 容易踩的三个坑
最常被误用的是对大文本字段(TextField)、二进制字段(BinaryField)或 JSON 字段(JSONField)做延迟,却忘了后续是否真需要它们。
- 延迟了
content字段,但在模板里写了{{ post.content|truncatewords:50 }}→ 触发一次额外查询,反而更慢 - 对多对多字段使用
defer('tags')没有效果,Django 不支持延迟关系字段(只会静默忽略) - 在
values()或values_list()之后再链式调用defer()会被忽略,因为此时已脱离模型实例层
性能差异到底有多大?
实测一个含 20 个字段的模型,其中 1 个 TextField 平均 8KB:
- 全字段查询(
Post.objects.all()):单条记录约 12ms,内存占用 ~14KB -
Post.objects.only('id', 'title', 'status'):单条降至 3ms,内存 ~2KB - 但若在循环中频繁访问被
only()排除的字段,总耗时可能飙升到 60ms+(N+1)
真正省资源的前提是:你确实不访问那些字段。否则只是把 IO 延后,还增加了查询调度开销。
Django 的字段延迟不是“懒加载优化”,而是“显式裁剪”。它不智能判断你是否需要,只忠实地按你的声明执行。一旦逻辑绕过声明(比如模板、序列化、中间件里偷偷读了 deferred 字段),优化就失效了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











