DevCN
菜单
讨论动态发现圈子我关注的我的收藏
# Python

QuerySet 是惰性的吗?什么时候才真正执行 SQL?only/values 怎么省查询?

阿青 · 社区话题账号 · · 1 次阅读

社区话题账号 · 用于整理公开问题与发起讨论,不代表真实个人经历。

是。QuerySet 构建时不查数据库,只有"求值"时才执行 SQL——这是 Django ORM 性能的核心机制。

qs = User.objects.filter(age__gt=18)   # 构建:不查库(惰性)
qs = qs.order_by("name")               # 继续叠加:仍不查库
print(len(qs))                         # 此时才执行 SQL

会触发 SQL 的求值操作:

| 操作 | 说明 |

|---|---|

| 迭代 for u in qs | 执行查询 |

| list(qs) / bool(qs) | 转列表/判空 |

| qs[0] / qs[:10] | 切片(切片也会查) |

| .count() .exists() .first() .last() | 聚合/取首尾 |

| len(qs) | 求长度(⚠️ 会加载全部记录再数,大表用 .count()) |

| .get() .filter().exists() | 立即执行 |

复用会缓存: 同一 QuerySet 被求值两次,第二次用缓存结果(不再查库):

qs = User.objects.filter(active=True)
list(qs)      # 第 1 次:执行 SQL
list(qs)      # 第 2 次:走缓存(除非数据变了且重新构建)

省查询三板斧:

1. `only()` / `defer()`:只加载需要的字段

# 列表页只需 name/email,别把大 text 字段全查出来
User.objects.only("id", "name", "email")     # 只 SELECT 这些
User.objects.defer("bio")                    # 排除大字段(等价反向)
# 注意:之后访问未加载字段会"额外查询",别在循环里访问

2. `values()` / `values_list()`:只要字典/元组,不构建模型对象(省对象实例化开销)

User.objects.filter(active=True).values("name", "email")        # 字典列表
User.objects.values_list("email", flat=True)                    # 一列值列表

3. 配合聚合: 能用 qs.count() 别用 len(qs);分组统计用 annotate,避免循环里逐条 count。

性能自查: django-debug-toolbar 看 SQL 数量;循环里出现"重复查询"就是 N+1(用 select_related/prefetch_related,见第 52 题)。

下载推广海报

圈内讨论推广海报

《QuerySet 是惰性的吗?什么时候才真正执行 SQL?only/values 怎么省查询?》完整推广海报
REPLIES

回复

0 条回复
暂无回复。