
.all() 对 QuerySet 会创建新克隆(不共享缓存),触发重复查询;但对 RelatedManager(如 obj.relationship.all())则直接返回已预取的缓存结果,不查库——关键区别在于调用对象是 QuerySet 还是 Manager。
.all() 对 queryset 会创建新克隆(不共享缓存),触发重复查询;但对 relatedmanager(如 `obj.relationship.all()`)则直接返回已预取的缓存结果,不查库——关键区别在于调用对象是 queryset 还是 manager。
在 Django 开发中,.all() 看似简单,却常引发性能困惑:它到底查数据库,还是复用缓存?答案取决于被调用的对象类型——这是理解其行为的核心。
✅ 情况一:.all() 调用于已存在的 QuerySet(会克隆 + 可能重复查询)
qs = Restaurant.objects.filter(active=True)
list(qs) # 第一次求值 → 执行 SQL 查询,并缓存结果到 qs 的内部 cache
list(qs.all()) # qs.all() 返回一个全新克隆的 QuerySet(cache 为空)
# → 再次执行相同 SQL,产生冗余查询!
原因在于:.all() 在 QuerySet 类中本质是 self._clone()(Django 源码证实)。克隆后的 QuerySet 拥有独立的 _result_cache,与原 QuerySet 互不影响。因此,即使原 QuerySet 已缓存结果,.all() 克隆体仍会重新触发数据库查询(除非你显式复用或提前缓存)。
⚠️ 注意:文档中“you can get updated results by calling
.all()”指的是主动放弃旧缓存、强制刷新的语义,而非“智能复用”。它是一种“重置式重查”,不是“惰性复用”。
✅ 情况二:.all() 调用于 RelatedManager(如 obj.foreign_key_set.all() 或 obj.many_to_many_field.all())→ 不查库,纯用缓存
restaurants = Restaurant.objects.prefetch_related('pizzas').all()
restaurant = restaurants[0]
# ✅ 安全:pizzas 是 PrefetchManager 实例,.all() 直接返回已 prefetch 的内存列表
pizzas = restaurant.pizzas.all() # 零查询!等价于 list(restaurant.pizzas.all())
此时 restaurant.pizzas 并非 QuerySet,而是一个 RelatedManager(具体为 PrefetchRelatedManager)。它的 .all() 方法不走 QuerySet 克隆逻辑,而是直接委托给 self.get_queryset() —— 而该方法在预取后会返回一个已填充 _result_cache 的 QuerySet(源自 prefetch_related 的预加载结果)。
源码佐证(Django RelatedManager.all):
def all(self):
return self.get_queryset() # ← 不克隆!直接返回预取好的 QuerySet(含缓存)
? 关键总结:一句话区分
| 调用主体 |
.all() 行为 |
是否查库 | 原因说明 |
|---|---|---|---|
QuerySet 实例 |
创建新克隆,_result_cache 为空 |
✅ 是 | 克隆不继承缓存,首次求值必查 |
RelatedManager 实例(如 obj.rel_set.all()) |
返回 get_queryset(),复用预取缓存 |
❌ 否 | Manager 层绕过克隆,直取结果 |
? 最佳实践建议
-
避免无谓
.all():对已赋值的 QuerySet,优先直接使用(如qs),而非qs.all(); -
预取后放心遍历:
prefetch_related/select_related后,关系属性的.all()是安全的; -
需刷新数据?显式处理:若需最新数据,应重新构造 QuerySet(如
Model.objects.filter(...)),而非依赖.all()的“重查”语义; -
调试技巧:开启
django.db.connection.queries或使用django-debug-toolbar,实时观察实际 SQL 发送次数。
理解这一差异,是写出高效、可预测 Django ORM 代码的关键一步。











