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

大表场景下通过prefetch与过滤器优化Django查询性能

解决方案

1. 优化自定义Manager

把原来分散的两个预取方法合并,同时预取当前在职的董事和会长数据,分别存入不同的自定义属性避免冲突:

from django.db.models import Prefetch

class OrganisationManager(models.Manager):
    def with_current_positions(self):
        # 预取当前在职董事数据,关联查询对应的人员信息
        director_query = Position.objects.select_related('person').filter(
            position_current=True,
            position_type="director"
        )
        # 预取当前在职会长数据,关联查询对应的人员信息
        president_query = Position.objects.select_related('person').filter(
            position_current=True,
            position_type="president"
        )
        return super().get_queryset().prefetch_related(
            Prefetch('position_set', queryset=director_query, to_attr="current_directors"),
            Prefetch('position_set', queryset=president_query, to_attr="current_presidents")
        )

2. 修改模型的职位获取方法

直接从预取好的属性中读取数据,不再调用filter触发新的数据库查询,同时修正原president方法中position_type值和模型定义不匹配的问题:

class Organisation(models.Model):
    organisation_name = models.CharField(max_length=255, blank=True, null=True)
    positions = models.ManyToManyField(Person, through='Position')
    objects =  OrganisationManager()

    def director(self):
        # 直接从预取的列表中取数据,无额外数据库查询
        return self.current_directors[-1] if self.current_directors else None
    
    def president(self):
        # 修正position_type取值为模型choices中定义的president,原代码的"P"为错误值
        return self.current_presidents[-1] if self.current_presidents else None

3. 调整ListView查询逻辑

重写ListView的get_queryset方法,调用我们优化后的带预取逻辑的Manager方法:

from django.views.generic import ListView
from .models import Organisation

class OrganisationListView(ListView):
    model = Organisation
    # 替换原有默认查询集
    def get_queryset(self):
        return Organisation.objects.with_current_positions()

4. 模板调用方式

原有模板的调用逻辑无需做任何修改,直接沿用之前的写法即可,示例模板代码如下:

<table>
  <thead>
    <tr>
      <th>机构名称</th>
      <th>会长</th>
      <th>会长任职开始日期</th>
      <th>董事</th>
      <th>董事任职开始日期</th>
    </tr>
  </thead>
  <tbody>
    {% for org in object_list %}
    <tr>
      <td>{{ org.organisation_name }}</td>
      <td>{{ org.president.person.full_name|default:"-" }}</td>
      <td>{{ org.president.position_start|default:"-" }}</td>
      <td>{{ org.director.person.full_name|default:"-" }}</td>
      <td>{{ org.director.position_start|default:"-" }}</td>
    </tr>
    {% endfor %}
  </tbody>
</table>

优化效果

优化前N条机构数据会产生2N+1次数据库查询,优化后仅需要3次查询:1次查询机构列表、2次分别查询预取的董事和会长职位及关联人员信息,完全避免了渲染循环中的重复查询问题,不会再出现Heroku超时的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 21:12:03