Django多映射表关联查询prefetch失效,仍存在500+查询问题
解决Django查询中subnet字段未预获取的问题
看起来你已经在Django查询优化上迈出了一大步——把查询量从1000+砍到500+,但卡在了subnet字段的预获取上,我来帮你捋捋问题出在哪以及怎么修复。
问题根源
你用Prefetch关联了circuit__devicecircuitsubnets_set这个反向关联,但默认情况下,Prefetch只会预取DeviceCircuitSubnet的实例。如果subnet是DeviceCircuitSubnet的外键字段,Django不会自动帮你预取这个关联对象,这就导致你后续访问每个DeviceCircuitSubnet的subnet属性时,还是会触发额外的数据库查询,也就是你看到的subnet没被预获取的情况。
修复方案
核心思路就是在Prefetch的queryset参数里,给DeviceCircuitSubnet加上select_related('subnet'),把subnet一起预取回来。修改后的代码应该是这样:
from django.db.models import Prefetch circuits = SiteCircuits.objects.all() \ .exclude(circuit__decommissioned=True) \ .select_related('site', 'circuit') # 合并两个select_related,代码更简洁 .prefetch_related( Prefetch( 'circuit__devicecircuitsubnets_set', # 关键:在DeviceCircuitSubnet的查询集里预取subnet queryset=DeviceCircuitSubnet.objects.select_related('subnet'), # 可选:给预取的集合重命名,避免和默认反向关联名混淆,方便后续调用 to_attr='prefetched_subnets' ) )
额外小贴士
- 如果你之前写的
DeviceCircuit...是DeviceCircuitSubnet的简写,记得补全完整的模型名称; - 用
to_attr给预取结果起个友好的名字(比如prefetched_subnets),之后访问的时候就可以用circuit.prefetched_subnets代替circuit.devicecircuitsubnets_set.all(),不仅代码更清晰,还能避免意外触发重复查询; - 推荐用
django-debug-toolbar来验证优化效果——如果之后访问subnet属性不再产生新的数据库查询,就说明你的预获取配置生效了。
内容的提问来源于stack exchange,提问作者AlexW
相关产品推荐
相关产品推荐

