大表场景下通过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
相关产品推荐
相关产品推荐

