This commit is contained in:
zty 2025-09-12 14:18:57 +08:00
commit aff39f3e31
5 changed files with 64 additions and 65 deletions

View File

@ -9,7 +9,6 @@ from django.utils.timezone import now
from user_agents import parse from user_agents import parse
import logging import logging
from rest_framework.response import Response from rest_framework.response import Response
from django.db import transaction
from rest_framework.exceptions import ParseError, ValidationError from rest_framework.exceptions import ParseError, ValidationError
from apps.utils.errors import PKS_ERROR from apps.utils.errors import PKS_ERROR
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
@ -19,6 +18,7 @@ from apps.utils.serializers import PkSerializer
from rest_framework.decorators import action from rest_framework.decorators import action
from apps.utils.serializers import ComplexSerializer from apps.utils.serializers import ComplexSerializer
from django.db.models import F from django.db.models import F
from django.db import transaction
# 实例化myLogger # 实例化myLogger
myLogger = logging.getLogger('log') myLogger = logging.getLogger('log')
@ -82,6 +82,7 @@ class BulkCreateModelMixin(CreateModelMixin):
def after_bulk_create(self, objs): def after_bulk_create(self, objs):
pass pass
@transaction.atomic
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""创建(支持批量) """创建(支持批量)
@ -91,10 +92,9 @@ class BulkCreateModelMixin(CreateModelMixin):
many = False many = False
if isinstance(rdata, list): if isinstance(rdata, list):
many = True many = True
with transaction.atomic(): sr = self.get_serializer(data=rdata, many=many)
sr = self.get_serializer(data=rdata, many=many) sr.is_valid(raise_exception=True)
sr.is_valid(raise_exception=True) self.perform_create(sr)
self.perform_create(sr)
if many: if many:
self.after_bulk_create(sr.data) self.after_bulk_create(sr.data)
return Response(sr.data, status=201) return Response(sr.data, status=201)
@ -105,6 +105,7 @@ class BulkUpdateModelMixin(UpdateModelMixin):
def after_bulk_update(self, objs): def after_bulk_update(self, objs):
pass pass
@transaction.atomic
def partial_update(self, request, *args, **kwargs): def partial_update(self, request, *args, **kwargs):
"""部分更新(支持批量) """部分更新(支持批量)
@ -113,6 +114,7 @@ class BulkUpdateModelMixin(UpdateModelMixin):
kwargs['partial'] = True kwargs['partial'] = True
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
@transaction.atomic
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
"""更新(支持批量) """更新(支持批量)
@ -124,16 +126,15 @@ class BulkUpdateModelMixin(UpdateModelMixin):
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
objs = [] objs = []
if isinstance(request.data, list): if isinstance(request.data, list):
with transaction.atomic(): for ind, item in enumerate(request.data):
for ind, item in enumerate(request.data): obj = get_object_or_404(queryset, id=item['id'])
obj = get_object_or_404(queryset, id=item['id']) sr = self.get_serializer(obj, data=item, partial=partial)
sr = self.get_serializer(obj, data=item, partial=partial) if not sr.is_valid():
if not sr.is_valid(): err_dict = { f'{ind+1}': sr.errors}
err_dict = { f'{ind+1}': sr.errors} raise ValidationError(err_dict)
raise ValidationError(err_dict) self.perform_update(sr) # 用自带的更新,可能需要做其他操作
self.perform_update(sr) # 用自带的更新,可能需要做其他操作 objs.append(sr.data)
objs.append(sr.data) self.after_bulk_update(objs)
self.after_bulk_update(objs)
else: else:
raise ParseError('提交数据非列表') raise ParseError('提交数据非列表')
return Response(objs) return Response(objs)
@ -148,6 +149,7 @@ class BulkUpdateModelMixin(UpdateModelMixin):
class BulkDestroyModelMixin(DestroyModelMixin): class BulkDestroyModelMixin(DestroyModelMixin):
@swagger_auto_schema(request_body=PkSerializer) @swagger_auto_schema(request_body=PkSerializer)
@transaction.atomic
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""删除(支持批量) """删除(支持批量)

View File

@ -1,6 +1,6 @@
from django.core.cache import cache from django.core.cache import cache
from django.http import StreamingHttpResponse from django.http import StreamingHttpResponse, Http404
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.mixins import RetrieveModelMixin from rest_framework.mixins import RetrieveModelMixin
@ -18,7 +18,8 @@ from apps.utils.serializers import ComplexSerializer
from rest_framework.throttling import UserRateThrottle from rest_framework.throttling import UserRateThrottle
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
import json import json
from django.db import connection
from django.core.exceptions import ObjectDoesNotExist
class CustomGenericViewSet(MyLoggingMixin, GenericViewSet): class CustomGenericViewSet(MyLoggingMixin, GenericViewSet):
""" """
@ -89,6 +90,34 @@ class CustomGenericViewSet(MyLoggingMixin, GenericViewSet):
elif hash_v_e: elif hash_v_e:
return Response(hash_v_e) return Response(hash_v_e)
def get_object(self, force_lock=False):
"""
智能加锁的get_object
- 只读请求普通查询
- 非只读请求且在事务中加锁查询
- 非只读请求但不在事务中普通查询带警告
"""
# 只读方法列表
read_only_methods = ['GET', 'HEAD', 'OPTIONS']
if self.request.method not in read_only_methods and connection.in_atomic_block:
if force_lock:
raise ParseError("当前操作需要在事务中进行,请使用事务装饰器")
# 非只读请求且在事务中:加锁查询
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
try:
obj = queryset.select_for_update().get(**filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
except ObjectDoesNotExist:
raise Http404
else:
# 其他情况:普通查询
return super().get_object()
def get_serializer_class(self): def get_serializer_class(self):
action_serializer_name = f"{self.action}_serializer_class" action_serializer_name = f"{self.action}_serializer_class"
action_serializer_class = getattr(self, action_serializer_name, None) action_serializer_class = getattr(self, action_serializer_name, None)
@ -194,4 +223,3 @@ class CustomModelViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, CustomListM
""" """
增强的ModelViewSet 增强的ModelViewSet
""" """
pass

View File

@ -738,7 +738,6 @@ class BatchSt(BaseModel):
# return ins, True # return ins, True
@classmethod @classmethod
@transaction.atomic
def init_dag(cls, batch:str): def init_dag(cls, batch:str):
""" """
更新批次数据关系链(初步) 更新批次数据关系链(初步)

View File

@ -885,7 +885,6 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
ftest_sr.update(instance=ftest, validated_data=ftest_data) ftest_sr.update(instance=ftest, validated_data=ftest_data)
return mlogbw return mlogbw
@transaction.atomic
def create(self, validated_data): def create(self, validated_data):
wpr: Wpr = validated_data.get("wpr", None) wpr: Wpr = validated_data.get("wpr", None)
if wpr: if wpr:
@ -898,7 +897,6 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
mlogbw = self.save_ftest(mlogbw, ftest_data) mlogbw = self.save_ftest(mlogbw, ftest_data)
return mlogbw return mlogbw
@transaction.atomic
def update(self, instance, validated_data): def update(self, instance, validated_data):
validated_data.pop("mlogb") validated_data.pop("mlogb")
ftest_data = validated_data.pop("ftest", None) ftest_data = validated_data.pop("ftest", None)
@ -1078,7 +1076,6 @@ class MlogbOutUpdateSerializer(CustomModelSerializer):
# else: # else:
# raise ParseError("mlogbdefect仅支持批次件") # raise ParseError("mlogbdefect仅支持批次件")
# return ins # return ins
@transaction.atomic
def update(self, instance, validated_data): def update(self, instance, validated_data):
mlogbdefect = validated_data.pop("mlogbdefect", None) mlogbdefect = validated_data.pop("mlogbdefect", None)
with transaction.atomic(): with transaction.atomic():

View File

@ -37,14 +37,12 @@ from django.db.models import Q
from apps.utils.tools import convert_ordereddict, update_dict from apps.utils.tools import convert_ordereddict, update_dict
from django.db.models import Count from django.db.models import Count
from datetime import datetime, timedelta from datetime import datetime, timedelta
from apps.utils.lock import lock_model_record_d_method
from apps.em.models import Equipment from apps.em.models import Equipment
from django.db.models import Prefetch from django.db.models import Prefetch
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi from drf_yasg import openapi
# Create your views here. # Create your views here.
@ -155,7 +153,6 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
return queryset.exclude(state=WMaterial.WM_SCRAP) return queryset.exclude(state=WMaterial.WM_SCRAP)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer)
@transaction.atomic
def batchs(self, request): def batchs(self, request):
"""获取车间的批次号(废弃) """获取车间的批次号(废弃)
@ -282,7 +279,6 @@ class MlogViewSet(CustomModelViewSet):
item["mlogbw_number_list"] = wpr_dict.get(item["id"], None) item["mlogbw_number_list"] = wpr_dict.get(item["id"], None)
return data return data
@lock_model_record_d_method(Mlog)
def perform_destroy(self, instance): def perform_destroy(self, instance):
if instance.submit_time is not None: if instance.submit_time is not None:
raise ParseError('日志已提交不可变动') raise ParseError('日志已提交不可变动')
@ -295,7 +291,6 @@ class MlogViewSet(CustomModelViewSet):
instance.delete() instance.delete()
Ftest.objects.filter(id__in=ftestIds).delete() Ftest.objects.filter(id__in=ftestIds).delete()
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
ins = serializer.instance ins = serializer.instance
if ins.ticket and ins.ticket.state != State.STATE_TYPE_START: if ins.ticket and ins.ticket.state != State.STATE_TYPE_START:
@ -318,7 +313,10 @@ class MlogViewSet(CustomModelViewSet):
ins = sr.save() ins = sr.save()
return Response(MlogSerializer(ins).data) return Response(MlogSerializer(ins).data)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.update'}, serializer_class=MlogChangeSerializer) @action(methods=['post'], detail=True,
perms_map={'post': 'mlog.update'},
serializer_class=MlogChangeSerializer)
@transaction.atomic
def change(self, request, *args, **kwargs): def change(self, request, *args, **kwargs):
"""修改日志 """修改日志
@ -334,7 +332,9 @@ class MlogViewSet(CustomModelViewSet):
sr.save() sr.save()
return Response(MlogSerializer(ins).data) return Response(MlogSerializer(ins).data)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=Serializer) @action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'},
serializer_class=Serializer)
@transaction.atomic
def submit(self, request, *args, **kwargs): def submit(self, request, *args, **kwargs):
"""日志提交(变动车间库存) """日志提交(变动车间库存)
@ -349,20 +349,12 @@ class MlogViewSet(CustomModelViewSet):
if p.mlog_need_ticket: if p.mlog_need_ticket:
raise ParseError('该日志需要审批!') raise ParseError('该日志需要审批!')
mlog_submit_validate(ins) mlog_submit_validate(ins)
with transaction.atomic(): mlog_submit(ins, self.request.user, now)
updated_count = Mlog.objects.filter(id=ins.id, submit_time__isnull=True).update( vdata_new = MlogSerializer(ins).data
submit_time=now, submit_user=request.user, update_by=request.user)
if updated_count == 1:
mlog_submit(ins, self.request.user, now)
else:
raise ParseError('记录正在处理中,请稍后再试')
vdata_new = MlogSerializer(ins).data
# create_auditlog('submit', ins, vdata_new,
# vdata_old, now, self.request.user)
return Response(vdata_new) return Response(vdata_new)
@action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=MlogRevertSerializer) @action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=MlogRevertSerializer)
@transaction.atomic
def revert(self, request, *args, **kwargs): def revert(self, request, *args, **kwargs):
"""撤回日志提交 """撤回日志提交
@ -377,20 +369,10 @@ class MlogViewSet(CustomModelViewSet):
if user != ins.submit_user: if user != ins.submit_user:
raise ParseError('非提交人不可撤销!') raise ParseError('非提交人不可撤销!')
now = timezone.now() now = timezone.now()
with transaction.atomic(): mlog_revert(ins, user, now)
updated_count = Mlog.objects.filter(id=ins.id, submit_time__isnull=False).update(
submit_time=None, update_time=now, submit_user=None, update_by=request.user)
if updated_count == 1:
mlog_revert(ins, user, now)
else:
raise ParseError('记录正在处理中,请稍后再试')
# create_auditlog('revert', ins, {}, {}, now, user,
# request.data.get('change_reason', ''))
return Response(MlogSerializer(instance=ins).data) return Response(MlogSerializer(instance=ins).data)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogRelatedSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogRelatedSerializer)
@transaction.atomic
def related_first(self, request, *args, **kwargs): def related_first(self, request, *args, **kwargs):
"""获取相关任务的第一道工序日志 """获取相关任务的第一道工序日志
@ -508,8 +490,7 @@ class HandoverViewSet(CustomModelViewSet):
'material__number', 'material__specification', 'batch', 'material__model', 'b_handover__batch', "new_batch", "wm__batch"] 'material__number', 'material__specification', 'batch', 'material__model', 'b_handover__batch', "new_batch", "wm__batch"]
prefetch_related_fields = ["b_handover"] prefetch_related_fields = ["b_handover"]
@lock_model_record_d_method(Handover) def perform_destroy(self, instance):
def perform_destroy(self, instance:Handover):
user = self.request.user user = self.request.user
if instance.submit_time is not None: if instance.submit_time is not None:
raise ParseError('该交接记录已提交不可删除') raise ParseError('该交接记录已提交不可删除')
@ -520,9 +501,8 @@ class HandoverViewSet(CustomModelViewSet):
ticket.delete() ticket.delete()
instance.delete() instance.delete()
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
ins:Handover = self.get_object() ins:Handover = serializer.instance
if ins.submit_time is not None: if ins.submit_time is not None:
raise ParseError('该交接记录已提交!') raise ParseError('该交接记录已提交!')
ticket:Ticket = ins.ticket ticket:Ticket = ins.ticket
@ -580,7 +560,6 @@ class HandoverViewSet(CustomModelViewSet):
return Response() return Response()
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=HandoverMgroupSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=HandoverMgroupSerializer)
@transaction.atomic
def mgroups(self, request, *args, **kwargs): def mgroups(self, request, *args, **kwargs):
"""获取可交接到的工段 """获取可交接到的工段
@ -708,7 +687,6 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
serializer_class = MlogbInSerializer serializer_class = MlogbInSerializer
update_serializer_class = MlogbInUpdateSerializer update_serializer_class = MlogbInUpdateSerializer
@transaction.atomic
def perform_destroy(self, instance): def perform_destroy(self, instance):
ins: Mlogb = instance ins: Mlogb = instance
if ins.mlog.submit_time is not None: if ins.mlog.submit_time is not None:
@ -716,7 +694,6 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
ins.delete() ins.delete()
ins.mlog.cal_mlog_count_from_mlogb() ins.mlog.cal_mlog_count_from_mlogb()
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
ins:Mlogb = serializer.save() ins:Mlogb = serializer.save()
ins.mlog.cal_mlog_count_from_mlogb() ins.mlog.cal_mlog_count_from_mlogb()
@ -883,7 +860,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
raise ParseError("不支持生成产出物料!") raise ParseError("不支持生成产出物料!")
mlog.cal_mlog_count_from_mlogb() mlog.cal_mlog_count_from_mlogb()
@transaction.atomic
def perform_create(self, serializer): def perform_create(self, serializer):
mlogbin: Mlogb = serializer.save() mlogbin: Mlogb = serializer.save()
MlogbInViewSet.p_create_after(mlogbin) MlogbInViewSet.p_create_after(mlogbin)
@ -925,7 +902,6 @@ class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):
queryset = Mlogb.objects.filter(material_out__isnull=False) queryset = Mlogb.objects.filter(material_out__isnull=False)
serializer_class = MlogbOutUpdateSerializer serializer_class = MlogbOutUpdateSerializer
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
material_out = serializer.validated_data.get('material_out') material_out = serializer.validated_data.get('material_out')
if material_out and material_out.tracking == Material.MA_TRACKING_SINGLE: if material_out and material_out.tracking == Material.MA_TRACKING_SINGLE:
@ -985,7 +961,6 @@ class MlogbwViewSet(CustomModelViewSet):
# raise ParseError('请指定所属消耗/产出明细') # raise ParseError('请指定所属消耗/产出明细')
# return super().filter_queryset(queryset) # return super().filter_queryset(queryset)
@transaction.atomic
def perform_create(self, serializer): def perform_create(self, serializer):
ins:Mlogbw = serializer.save() ins:Mlogbw = serializer.save()
mlog:Mlog = None mlog:Mlog = None
@ -1061,7 +1036,6 @@ class MlogbwViewSet(CustomModelViewSet):
raise ParseError("该个号不可产生该批") raise ParseError("该个号不可产生该批")
mlog.cal_mlog_count_from_mlogb() mlog.cal_mlog_count_from_mlogb()
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
mlogbw = serializer.save() mlogbw = serializer.save()
if isinstance(mlogbw, list): if isinstance(mlogbw, list):
@ -1079,7 +1053,6 @@ class MlogbwViewSet(CustomModelViewSet):
mlog = mlogb.mlog mlog = mlogb.mlog
mlog.cal_mlog_count_from_mlogb() mlog.cal_mlog_count_from_mlogb()
@transaction.atomic
def perform_destroy(self, instance:Mlogbw): def perform_destroy(self, instance:Mlogbw):
mlogb:Mlogb = instance.mlogb mlogb:Mlogb = instance.mlogb
if mlogb.material_out is not None and instance.wpr is not None: if mlogb.material_out is not None and instance.wpr is not None: