CodeWalk

Django QuerySet惰性求值及N+1查询优化

作者:屠龙少年 · 2026-05-30 12:55

请解释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 querysetbool(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))