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 题)。
回复
0 条回复