feat: mioitem 返回 defect/defect_name/okcate 并优化 mio 详情 items 序列化

- MIOItemSerializer 新增 defect/defect_name/okcate,取数优先级 wm.defect>mb.defect,
  与 services.py 中 do_out/do_in 的取数逻辑保持一致
- MIOItemViewSet.select_related_fields 加 wm__defect/mb__defect 消除 N+1
- MIODetailSerializer.items 改用 MIOItemForMioDetailSerializer 轻量序列化器
  (仅含前端实际消费的 id/mio/material/material_/batch/count/pack_index),
  避开 MIOItemSerializer 中 assemb/mioitemw 的 N+1
- MIOViewSet.retrieve 时挂 Prefetch('item_mio') 进一步减少查询

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-05-12 09:23:22 +08:00
parent 70f2d0d2dd
commit bb64c89d76
2 changed files with 50 additions and 4 deletions

View File

@ -264,6 +264,21 @@ class MIOItemListSimpleSerializer(CustomModelSerializer):
model = MIOItem model = MIOItem
fields = ["id", "mio", "material", "warehouse", "material_name", "warehouse_name", "batch", "count", "test_date", "count_notok"] fields = ["id", "mio", "material", "warehouse", "material_name", "warehouse_name", "batch", "count", "test_date", "count_notok"]
class _MioDetailItemMaterialSerializer(CustomModelSerializer):
class Meta:
model = Material
fields = ["id", "name", "model"]
class MIOItemForMioDetailSerializer(CustomModelSerializer):
"""MIO 详情下挂的 items 用,仅含前端实际消费的字段"""
material_ = _MioDetailItemMaterialSerializer(source='material', read_only=True)
class Meta:
model = MIOItem
fields = ["id", "mio", "material", "material_", "batch", "count", "pack_index"]
class MIOItemSerializer(CustomModelSerializer): class MIOItemSerializer(CustomModelSerializer):
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True) warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
material_ = MaterialSerializer(source='material', read_only=True) material_ = MaterialSerializer(source='material', read_only=True)
@ -273,11 +288,33 @@ class MIOItemSerializer(CustomModelSerializer):
inout_date = serializers.DateField(source='mio.inout_date', read_only=True) inout_date = serializers.DateField(source='mio.inout_date', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True) test_user_name = serializers.CharField(source='test_user.name', read_only=True)
mioitemw = MIOItemwSerializer(many=True, required=False) mioitemw = MIOItemwSerializer(many=True, required=False)
defect = serializers.SerializerMethodField(label="缺陷")
defect_name = serializers.SerializerMethodField(label="缺陷名称")
okcate = serializers.SerializerMethodField(label="缺陷分类")
class Meta: class Meta:
model = MIOItem model = MIOItem
fields = '__all__' fields = '__all__'
def _resolve_defect(self, obj):
if obj.wm_id and obj.wm.defect_id:
return obj.wm.defect
if obj.mb_id and obj.mb.defect_id:
return obj.mb.defect
return None
def get_defect(self, obj):
d = self._resolve_defect(obj)
return d.id if d else None
def get_defect_name(self, obj):
d = self._resolve_defect(obj)
return d.name if d else None
def get_okcate(self, obj):
d = self._resolve_defect(obj)
return d.okcate if d else None
def to_representation(self, instance): def to_representation(self, instance):
ret = super().to_representation(instance) ret = super().to_representation(instance)
ret["price"] = None ret["price"] = None
@ -424,7 +461,7 @@ class MIOOtherSerializer(CustomModelSerializer):
class MIODetailSerializer(MIOListSerializer): class MIODetailSerializer(MIOListSerializer):
items = MIOItemSerializer(source='item_mio', many=True, read_only=True) items = MIOItemForMioDetailSerializer(source='item_mio', many=True, read_only=True)
class MIOItemTestSerializer(CustomModelSerializer): class MIOItemTestSerializer(CustomModelSerializer):

View File

@ -7,7 +7,7 @@ from django.conf import settings
from rest_framework import serializers from rest_framework import serializers
from django.utils import timezone from django.utils import timezone
from rest_framework.response import Response from rest_framework.response import Response
from django.db.models import Sum from django.db.models import Sum, Prefetch
from apps.inm.models import WareHouse, MaterialBatch, MIO, MIOItem, MIOItemw, Pack from apps.inm.models import WareHouse, MaterialBatch, MIO, MIOItem, MIOItemw, Pack
from apps.inm.serializers import ( from apps.inm.serializers import (
@ -153,6 +153,15 @@ class MIOViewSet(CustomModelViewSet):
'item_mio__a_mioitem__batch'] 'item_mio__a_mioitem__batch']
data_filter = True data_filter = True
def get_queryset(self):
queryset = super().get_queryset()
if self.action == 'retrieve':
queryset = queryset.prefetch_related(Prefetch(
'item_mio',
queryset=MIOItem.objects.select_related('material'),
))
return queryset
@classmethod @classmethod
def lock_and_check_can_update(cls, mio:MIO): def lock_and_check_can_update(cls, mio:MIO):
if not connection.in_atomic_block: if not connection.in_atomic_block:
@ -346,7 +355,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
serializer_class = MIOItemSerializer serializer_class = MIOItemSerializer
retrieve_serializer_class = MioItemDetailSerializer retrieve_serializer_class = MioItemDetailSerializer
create_serializer_class = MIOItemCreateSerializer create_serializer_class = MIOItemCreateSerializer
select_related_fields = ['warehouse', 'mio', 'material', 'test_user'] select_related_fields = ['warehouse', 'mio', 'material', 'test_user', 'wm__defect', 'mb__defect']
filterset_fields = { filterset_fields = {
"warehouse": ["exact"], "warehouse": ["exact"],
"mio": ["exact"], "mio": ["exact"],