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

Django多对多字段prefetch_related失效引发N+1查询优化问题

问题根因

你用prefetch_related预加载的是Carrera模型的实例对象,会缓存在estandarProducto.carreras的查询结果集中,但你调用.values()方法时,Django ORM 不会复用已缓存的实例,会重新向数据库发起查询,所以每遍历一个EstandarProducto实例就会多一次查询,导致总查询次数暴增。

修复方案

第一步:优化预加载查询

调整EstandarProducto的查询语句,预加载carreras时只取你需要的codigo字段,减少不必要的字段查询开销:

estandares = EstandarProducto.objects.select_related('recinto','producto','proveedor')\
    .prefetch_related(Prefetch('carreras', queryset=Carrera.objects.only('codigo')))\
    .filter(recinto=id_recinto)

这里only('codigo')表示预查询Carrera时只拉取id和codigo两个字段(id是关联必需字段,默认会查),比查询全表字段效率更高。

第二步:调整遍历逻辑,复用预加载缓存

把原遍历carreras的逻辑从调用.values()改为直接遍历预加载好的实例,这样就不会发起新的数据库查询:

# 原代码
# for carrera in estandarProducto.carreras.values():
#     print("Codigo: " ,carrera.get("codigo"))
#     carreras.append(carrera.get("codigo"))

# 替换为
for carrera in estandarProducto.carreras.all():
    print("Codigo: ", carrera.codigo)
    carreras.append(carrera.codigo)

修改后carreras的所有数据都来自预加载的缓存,不会再产生额外的查询请求。

额外性能优化建议

你当前代码中嵌套遍历productos_en_inventario的逻辑时间复杂度是O(n*m),数据量变大后也会成为性能瓶颈,可以调整为字典映射的方式降低复杂度:

  1. 把原productos_en_inventario = inventario.values()替换为:
# 以producto的id为key,预存库存数据,后续可以O(1)速度取值
producto_inventario_map = {
    item['id']: item 
    for item in inventario.values('id', 'cantidad', 'ultimo_año_reposicion')
}
  1. 把原遍历productos_en_inventario的嵌套循环替换为直接取字典值:
# 原嵌套循环代码直接删除,替换为以下逻辑
inventario_item = producto_inventario_map.get(estandarProducto.producto.id, {})
cantidad_inventario = inventario_item.get('cantidad', 0)
año_compra = inventario_item.get('ultimo_año_reposicion', '')
id_inventario_producto = inventario_item.get('id', '')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 16:39:04