perf:mlog/handover/mlogb列表消除N+1,select_related精简改prefetch,修Handover.handoverb属性绕过prefetch的bug

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-16 14:41:46 +08:00
parent 45c61efdc6
commit 5881c32c38
2 changed files with 23 additions and 26 deletions

View File

@ -721,7 +721,8 @@ class Handover(CommonADModel):
@property @property
def handoverb(self): def handoverb(self):
return Handoverb.objects.filter(handover=self) # 用反向关联管理器, 以命中 list 接口的 Prefetch("b_handover") 缓存, 避免 N+1
return self.b_handover.all()
class Handoverb(BaseModel): class Handoverb(BaseModel):
"""TN: 子级交接记录 """TN: 子级交接记录

View File

@ -285,6 +285,7 @@ class MlogViewSet(CustomModelViewSet):
"handle_user", "handle_user",
"equipment", "equipment",
"mgroup", "mgroup",
"mgroup__belong_dept",
"material_in", "material_in",
"material_out", "material_out",
"material_in__process", "material_in__process",
@ -298,7 +299,22 @@ class MlogViewSet(CustomModelViewSet):
# 'equipment_2', 'material_in', 'material_out', 'route__routepack', 'submit_user', # 'equipment_2', 'material_in', 'material_out', 'route__routepack', 'submit_user',
# 'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user', 'team', # 'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user', 'team',
# 'material_in__process', 'material_out__process', "shift", "route"] # 'material_in__process', 'material_out__process', "shift", "route"]
prefetch_related_fields = ["handle_users", "material_outs", "equipments"] prefetch_related_fields = [
"handle_users",
"material_outs",
"equipments",
# 以下为可选的一对一/外键, 用 prefetch(每个关系仅 1 条 IN 查询)避免 N+1
# 又不加宽主查询的 JOIN
"team",
"supplier",
"mtask",
"shift",
"test_user",
"handle_user_2",
"handle_leader",
"equipment_2",
"ticket__state",
]
filterset_class = MlogFilter filterset_class = MlogFilter
search_fields = [ search_fields = [
"material_in__name", "material_in__name",
@ -342,27 +358,7 @@ class MlogViewSet(CustomModelViewSet):
] ]
) )
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
from django.db import connection return super().list(request, *args, **kwargs)
from django.conf import settings
# 清空之前的查询记录
connection.queries_log.clear()
# 调用父类的 list 方法
response = super().list(request, *args, **kwargs)
# 获取执行的 SQL 查询
queries = connection.queries
sql_queries = []
for query in queries:
sql_queries.append({"sql": query["sql"], "time": query["time"], "params": query.get("params", [])})
# 将 SQL 信息添加到响应中(仅在调试模式)
if settings.DEBUG:
response.data["_debug"] = {"sql_queries": sql_queries, "total_queries": len(queries), "total_time": sum(float(q["time"]) for q in queries)}
return response
def add_info_for_list(self, data): def add_info_for_list(self, data):
czx_dict = {} czx_dict = {}
@ -590,10 +586,10 @@ class HandoverViewSet(CustomModelViewSet):
queryset = Handover.objects.all() queryset = Handover.objects.all()
list_serializer_class = HandoverListSerializer list_serializer_class = HandoverListSerializer
serializer_class = HandoverSerializer serializer_class = HandoverSerializer
select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material"] select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material", "material__process"]
filterset_class = HandoverFilter filterset_class = HandoverFilter
search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"] search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"]
prefetch_related_fields = [Prefetch("b_handover", queryset=Handoverb.objects.select_related("wm__defect"))] prefetch_related_fields = [Prefetch("b_handover", queryset=Handoverb.objects.select_related("wm__defect")), "ticket__state"]
def perform_destroy(self, instance: Handover): def perform_destroy(self, instance: Handover):
user = self.request.user user = self.request.user
@ -782,7 +778,7 @@ class MlogbViewSet(CustomListModelMixin, CustomGenericViewSet):
perms_map = {"get": "*"} perms_map = {"get": "*"}
queryset = Mlogb.objects.all() queryset = Mlogb.objects.all()
serializer_class = MlogbDetailSerializer serializer_class = MlogbDetailSerializer
select_related_fields = ["material_out", "material_in", "test_user", "wm_in__defect"] select_related_fields = ["material_out", "material_out__process", "material_in", "material_in__process", "test_user", "wm_in__defect"]
prefetch_related_fields = [ prefetch_related_fields = [
Prefetch("mlogbdefect_set", queryset=MlogbDefect.objects.select_related("defect")), Prefetch("mlogbdefect_set", queryset=MlogbDefect.objects.select_related("defect")),
] ]