Django QuerySet惰性求值及N+1查询优化
请解释Django ORM中QuerySet的惰性求值(Lazy Evaluation)机制,以及select_related和prefetch_related如何解决N+1查询问题。
回答
屠龙少年
惰性求值:Django的QuerySet在创建时不会立即执行数据库查询,只有在以下情况才会触发SQL执行(称为求值):
- 迭代:
for obj in queryset - 切片:
queryset[0:10](完整切片不会触发的说法不准确,实际上会) - 序列化:
list(queryset)、pickle - 布尔判断:
if queryset、bool(queryset) - 显示:
repr()、len()(len()会缓存结果)
N+1查询问题:查询主模型列表时,访问每行的关联字段触发额外查询。
# 问题:N+1次查询
for article in Article.objects.all(): # 1次
print(article.author.name) # N次
解决方案:
select_related()— SQL JOIN方式,适用于一对一、外键(正向关系)Article.objects.select_related('author').all() # LEFT JOIN author ON → 1次查询prefetch_related()— 额外查询后Python侧关联,适用于多对多、反向外键Article.objects.prefetch_related('tags').all() # 1次查文章 + 1次查标签 → 2次查询Prefetch对象:更精细控制,如Prefetch('comments', queryset=Comment.objects.filter(approved=True))