Django Admin预取失效:重复查询近千次问题排查
Django Admin预取(prefetch)失效排查及方法存放建议
我来帮你解决预取失效导致重复查询的问题,顺便解答你关于模型方法存放的疑问~
一、预取失效的原因及修复
你遇到的重复查询问题,核心原因是在get_queryset方法里重新创建了一个全新的QuerySet,而没有基于Admin返回的原始QuerySet进行链式处理。Django Admin内部会对QuerySet做权限校验、搜索过滤等操作,直接用SiteSubnets.objects创建的QuerySet会被Admin的逻辑覆盖,导致你配置的预取完全不生效。
修复后的admin.py代码:
class SiteSubnetsAdmin(admin.ModelAdmin): search_fields = ['site_data','device_data','subnet','subnet_type','vlan_id','peer_desc'] list_display = ('site_data','device_data','subnet','subnet_type','vlan_id','peer_desc') ordering = ('site_data','device_data',) def get_queryset(self, request): # 先获取Admin处理后的基础QuerySet(包含权限、过滤等) queryset = super().get_queryset(request) # 在基础QuerySet上添加预取配置 queryset = queryset.prefetch_related( Prefetch( 'circuit', queryset=Circuits.objects.prefetch_related( 'site_data', 'service_contacts', 'circuit_type', 'provider' ) ), 'site_data', 'device_data', 'subnet_type' ) return queryset admin.site.register(SiteSubnets, SiteSubnetsAdmin)
关键优化点:
- 基于Super的QuerySet扩展:用
super().get_queryset(request)获取Admin的基础QuerySet,再在这个基础上添加预取,确保预取逻辑能被Admin实际使用。 - 合并预取调用:把多次
prefetch_related调用合并成一次,代码更简洁,逻辑更清晰。 - 关联预取全覆盖:你的
Circuits模型的__str__方法用到了site_data.location、provider、circuit_type,已经通过Prefetch里的prefetch_related覆盖了这些关联,修复后不会再触发重复查询。
修复后,原本重复执行的SELECT "sites_sitedata"..."WHERE id='7'"这类查询会被预取的批量查询替代,大幅减少数据库请求次数。
二、模型方法存放的建议
你提到的full_info方法,最适合放在Circuits模型类内部,作为模型的实例方法,理由如下:
- 逻辑内聚:
full_info是基于Circuits实例的属性生成的,和模型本身紧密相关,放在模型类里符合面向对象设计。 - 调用方便:在Admin、模板或其他业务代码中,直接通过
circuit_instance.full_info()就能调用。
调整后的circuits.models代码:
class Circuits(models.Model): # ... 现有字段定义 ... def full_info(self): return '{} | {} | {} | {}'.format(self.site_data.location, self.provider, self.circuit_type, self.ref_no) def __str__(self): return '{} | {} | {}'.format(self.provider, self.circuit_type, self.ref_no) # ... 现有Meta类和其他方法 ...
如果后续这个方法需要被多个模型复用,或者逻辑过于复杂(比如涉及多个模型的复杂计算),再考虑把它放到外部的工具模块中,但目前的场景下,放在模型内部是最优解。
内容的提问来源于stack exchange,提问作者AlexW
相关产品推荐
相关产品推荐

