You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中外键对象获取:prefetch_related与model_set对比及性能分析

在Django中获取外键关联对象的最佳实践:性能与流程对比

结论先行:在视图层使用prefetch_related预取关联对象是性能更优的方案,能彻底避免模板直接调用model_set.all()带来的N+1查询性能问题。

两种方式的核心差异

1. 模板中直接调用model_set.all():性能陷阱

当你在模板里循环父对象并逐个调用object.book_set.all()时,Django会为每个父对象单独发起一次SQL查询。比如你有100个父对象,就会先执行1次查询获取所有父对象,再执行100次查询分别获取每个父对象的关联数据——这就是典型的N+1查询问题,父对象数量越多,数据库查询次数暴增,页面响应速度会急剧下降。

2. 视图层使用prefetch_related:高效预取

prefetch_related的工作逻辑是:

  • 先执行1次SQL查询获取所有父对象;
  • 再执行1次SQL查询获取所有与这些父对象关联的子对象;
  • 最后在Python内存层面自动将父对象和子对象关联起来。

无论父对象有多少个,总共只需要2次SQL查询,能极大降低数据库负载,提升页面性能。

代码示例对比

假设我们有以下模型:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='book_set')
不推荐的写法(模板直接调用)

views.py:

def author_list(request):
    authors = Author.objects.all()
    return render(request, 'author_list.html', {'authors': authors})

模板author_list.html:

{% for author in authors %}
    <h3>{{ author.name }}</h3>
    {% for book in author.book_set.all %}
        <p>{{ book.title }}</p>
    {% endfor %}
{% endfor %}

若有100个作者,会产生101次SQL查询,性能极低。

推荐的写法(视图层预取)

views.py:

def author_list(request):
    authors = Author.objects.all().prefetch_related('book_set')
    return render(request, 'author_list.html', {'authors': authors})

模板代码和上面完全一致,但此时仅需2次SQL查询,性能提升显著。

补充说明

  • 如果是正向外键关联(比如从Book查询对应的Author),应该使用select_related,它通过SQL JOIN语句一次性将主表和关联表的数据查询出来,比prefetch_related更高效。
  • 模板直接调用的方式仅适合关联对象极少的测试场景,生产环境务必避免。

内容的提问来源于stack exchange,提问作者Zainab Imad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:29:56