深入解析 Django ORM 查询执行阶段 的核心机制,包括查询集的惰性特性、表达式树构建、SQL 编译过程及优化原理。以下是详细分析:
一、查询集(QuerySet)的惰性执行机制
1. 惰性特性的底层实现
- 核心类:
django.db.models.query.QuerySet
- 关键属性:
query
:存储查询逻辑的django.db.models.sql.Query
对象_result_cache
:结果缓存(初始为None
,首次求值后填充)
- 惰性触发逻辑:
# 创建查询集(未执行 SQL) qs = Book.objects.filter(author="Doubao") # qs.query 对象此时包含: # { # 'model': Book, # 'where': [AND, =(author, "Doubao")], # 'limit': None, # 'offset': None, # 'order_by': [] # }
2. 链式操作原理
- 每次调用
filter()
、exclude()
、order_by()
等方法时:- 创建新的
QuerySet
对象 - 复制原
QuerySet
的query
对象 - 在新
query
对象上添加操作
- 创建新的
- 示例代码:
qs1 = Book.objects.filter(author="Doubao") # QuerySet A qs2 = qs1.order_by("-price") # QuerySet B(复制 QuerySet A 的 query 并添加排序) # qs2.query 现在包含: # { # 'model': Book, # 'where': [AND, =(author, "Doubao")], # 'order_by': ["-price"] # }
二、查询表达式树(Query Expression Tree)
1. 核心组件
WhereNode
类:表示 SQL 的WHERE
子句- 操作类型:
AND
、OR
、NOT
- 子节点:可以是其他
WhereNode
或具体条件
- 操作类型:
Lookup
类:表示具体的查询条件(如author="Doubao"
)- 包含:字段、操作符(如
exact
、gt
)、值
- 包含:字段、操作符(如
2. 复杂查询树构建示例
# Python 查询
books = Book.objects.filter(
Q(author="Doubao") & Q(price__gt=50) | Q(published_date__year=2023)
)
# 对应的表达式树结构(简化表示):
# WhereNode(connector=OR):
# ├─ WhereNode(connector=AND):
# │ ├─ Lookup(field=author, lookup=exact, value="Doubao")
# │ └─ Lookup(field=price, lookup=gt, value=50)
# └─ Lookup(field=published_date__year, lookup=exact, value=2023)
三、SQL 编译过程
1. 触发编译的操作
以下方法会触发 SQL 编译和执行:
- 迭代操作:
for book in queryset
- 切片操作:
queryset[0:10]
(带步长的切片会强制求值) - 聚合方法:
count()
、exists()
、aggregate()
- 获取单个对象:
first()
、last()
、get()
2. SQLCompiler 工作流程
- 核心类:
django.db.backends.*.compiler.SQLCompiler
- 编译步骤:
- 生成 SELECT 列表:根据
values()
或模型字段确定返回列 - 构建 FROM 子句:处理表名和别名
- 编译 WHERE 子句:将
WhereNode
转换为 SQL 条件 - 处理 JOIN:根据关联关系生成 INNER JOIN/LEFT OUTER JOIN
- 添加 LIMIT/OFFSET:处理分页逻辑
- 生成 ORDER BY:根据
order_by()
生成排序规则
- 生成 SELECT 列表:根据
3. 参数化查询实现
- 所有动态值通过占位符传递,防止 SQL 注入:
# Python 查询 Book.objects.filter(title__contains="Django") # 生成的 SQL(PostgreSQL 示例) SELECT "books"."id", "books"."title" FROM "books" WHERE "books"."title" LIKE %s; # 参数:['%Django%']
四、聚合查询的实现
1. annotate() 方法
- 实现原理:
- 在 SELECT 列表中添加聚合表达式
- 自动添加 GROUP BY 子句(如果需要)
- 示例:
# 查询每本书的评论数 books = Book.objects.annotate(review_count=Count("reviews")) # 生成的 SQL(简化) SELECT "books"."id", "books"."title", COUNT("reviews"."id") AS "review_count" FROM "books" LEFT OUTER JOIN "reviews" ON ("books"."id" = "reviews"."book_id") GROUP BY "books"."id";
2. F() 表达式与数据库计算
- 作用:直接在 SQL 中引用字段值,避免 Python 层面的数据传输
- 示例:
# 将所有书的价格提高 10% Book.objects.update(price=F("price") * 1.1) # 生成的 SQL UPDATE "books" SET "price" = "books"."price" * 1.1;
五、优化机制
1. 查询缓存
- 一级缓存:
QuerySet._result_cache
- 首次求值后缓存结果
- 重复访问直接返回缓存
- 示例:
qs = Book.objects.all() list(qs) # 执行 SQL 并缓存结果 list(qs) # 直接从缓存获取,不执行 SQL
2. 延迟加载与预加载
- 延迟加载(Lazy Loading):
book = Book.objects.first() reviews = book.reviews.all() # 触发额外 SQL 查询
- 预加载(Eager Loading):
# 使用 prefetch_related() 减少查询次数 books = Book.objects.prefetch_related("reviews") for book in books: reviews = book.reviews.all() # 无需额外查询
六、源码分析示例
1. QuerySet.filter() 方法
# django/db/models/query.py
class QuerySet:
def filter(self, *args, **kwargs):
return self._filter_or_exclude(False, *args, **kwargs)
def _filter_or_exclude(self, negate, *args, **kwargs):
clone = self._chain() # 创建 QuerySet 副本
clone.query.add_q(Q(*args, **kwargs)) # 添加查询条件到 query 对象
if negate:
clone.query.negate()
return clone
2. SQLCompiler.as_sql() 方法
# django/db/backends/sqlite3/compiler.py
class SQLCompiler:
def as_sql(self):
# 生成 SELECT 列表
select, select_params = self.get_select()
# 生成 FROM 子句
from_, from_params = self.get_from_clause()
# 生成 WHERE 子句
where, where_params = self.compile(self.query.where)
# 生成 ORDER BY 子句
order_by, order_params = self.get_order_by()
# 组装完整 SQL
sql = "SELECT %s FROM %s" % (select, from_)
if where:
sql += " WHERE %s" % where
if order_by:
sql += " ORDER BY %s" % order_by
return sql, select_params + from_params + where_params + order_params
七、常见性能陷阱与优化
1. N+1 查询问题
- 问题场景:
# 触发 1 次查询获取所有书 books = Book.objects.all() # 遍历每本书时触发 N 次查询获取作者 for book in books: print(book.author.name) # 每次循环触发一次 SQL
- 优化方案:
# 使用 select_related() 减少查询次数到 1 次 books = Book.objects.select_related("author") for book in books: print(book.author.name) # 无需额外查询
2. 过度使用 values()
- 错误做法:
# 返回字典,失去模型方法 data = Book.objects.values("id", "title")
- 优化方案:
# 使用 only() 仅加载需要的字段,保留模型实例 books = Book.objects.only("id", "title")