perf(wpm): batch load handover inventory validation

This commit is contained in:
caoqianming 2026-08-07 11:15:22 +08:00
parent d98b7fada2
commit 24ce008d3a
2 changed files with 98 additions and 0 deletions

View File

@ -1265,7 +1265,26 @@ class Handoverbwserializer(CustomModelSerializer):
read_only_fields = EXCLUDE_FIELDS_BASE + ["handoverb", "number"]
extra_kwargs = {'wpr': {'required': True}}
class CachedWMaterialPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
cache = getattr(self.root, "_handover_wmaterial_cache", None)
if cache is None:
return super().to_internal_value(data)
if not isinstance(data, (str, int)):
self.fail("incorrect_type", data_type=type(data).__name__)
try:
return cache[str(data)]
except KeyError:
self.fail("does_not_exist", pk_value=data)
class HandoverbSerializer(CustomModelSerializer):
wm = CachedWMaterialPrimaryKeyRelatedField(
queryset=WMaterial.objects.select_related(
"material", "defect", "mgroup", "belong_dept"
)
)
notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
defect_name = serializers.CharField(source="wm.defect.name", read_only=True)
handoverbw = Handoverbwserializer(many=True, required=False)
@ -1301,6 +1320,27 @@ class HandoverSerializer(CustomModelSerializer):
wm_notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
handoverb = HandoverbSerializer(many=True, required=False)
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
def to_internal_value(self, data):
handoverb = data.get("handoverb", []) if hasattr(data, "get") else []
wm_ids = {
str(item["wm"])
for item in handoverb
if isinstance(item, dict) and item.get("wm") is not None
}
if not wm_ids:
return super().to_internal_value(data)
queryset = WMaterial.objects.select_related(
"material", "defect", "mgroup", "belong_dept"
)
self._handover_wmaterial_cache = {
str(pk): instance for pk, instance in queryset.in_bulk(wm_ids).items()
}
try:
return super().to_internal_value(data)
finally:
del self._handover_wmaterial_cache
def validate(self, attrs):
if "mtype" not in attrs:

View File

@ -0,0 +1,58 @@
from django.db import connection
from django.test import TestCase
from django.test.utils import CaptureQueriesContext
from apps.mtm.models import Material
from apps.qm.models import Defect
from apps.system.models import Dept, User
from apps.wpm.models import Handover, WMaterial
from apps.wpm.serializers import HandoverSerializer
class HandoverSerializerQueryTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.dept = Dept.objects.create(name="合批查询测试车间")
cls.user = User.objects.create_user(username="handover-query-user")
cls.material = Material.objects.create(name="合批查询测试物料")
cls.defect_b = Defect.objects.create(
name="合批查询测试B类缺陷",
cate=Defect.cate_list[0],
okcate=Defect.DEFECT_OK_B,
)
cls.inventories = [
WMaterial.objects.create(
material=cls.material,
batch=f"QUERY-{index}",
count=1,
state=WMaterial.WM_OK,
defect=cls.defect_b if index == 2 else None,
belong_dept=cls.dept,
)
for index in range(3)
]
def test_handover_inventory_is_loaded_in_one_query(self):
serializer = HandoverSerializer(data={
"send_date": "2026-08-07",
"send_user": self.user.id,
"send_dept": self.dept.id,
"recive_dept": self.dept.id,
"handoverb": [
{"wm": inventory.id, "count": 1}
for inventory in self.inventories
],
"new_batch": "QUERY-MERGED",
"clear_defect": True,
"type": Handover.H_NORMAL,
"mtype": Handover.H_MERGE,
})
with CaptureQueriesContext(connection) as queries:
self.assertTrue(serializer.is_valid(), serializer.errors)
inventory_queries = [
query["sql"] for query in queries
if 'FROM "wpm_wmaterial"' in query["sql"]
]
self.assertEqual(len(inventory_queries), 1, inventory_queries)