Django中@action装饰器后的函数逻辑被忽略问题求助
Django中自定义POST请求逻辑不生效的解决方法
问题场景
实现LeadsViewSet视图集时,使用@action装饰器定义的create_lead方法在发起POST请求时完全被忽略,请求直接执行CreateModelMixin提供的默认POST逻辑并返回对象,需要让请求遵循自定义的业务逻辑。
用户代码如下:
from django.utils.translation import ugettext_lazy as _ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import mixins, serializers, viewsets from rest_framework.viewsets import ModelViewSet from apps.units.models import Unit from apps.api.mixins import DestroyModelMixin from apps.api.v1.decorators import action from apps.leads.models import Leads from .serializers import LeadSerializer from apps.api.v1.units.serializers import UnitSerializer class LeadsViewSet( mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin, DestroyModelMixin, ): serializer_class = LeadSerializer queryset = Leads.objects.all() @action(methods=["POST"], detail=False,) def create_lead(self, request, pk=None, *args, **kwargs): try: date = request.body.get("date") user_id = request.body.get("user_id") unit_id = request.body.get("unit_id") existing_lead = self.queryset.filter(user_id=user_id, unit_id=unit_id).exists() if existing_lead: raise serializers.ValidationError(_("Lead already exists")) Leads.objects.create( user_id=user_id, unit_id=unit_id, date=date ) return ({"message":"Lead Created Succesfully"}) except Exception as e: raise serializers.ValidationError(_("Lead not created"))
问题原因
视图集继承了mixins.CreateModelMixin,这个混入类会自动处理视图集根路径(如/leads/)的POST请求,执行默认的create方法。而@action装饰器定义的方法默认会生成子路径(如/leads/create_lead/),直接POST到根路径时,会优先触发CreateModelMixin的逻辑,而非自定义方法。
解决办法
方法1:重写默认create方法(推荐)
如果需要替换根路径POST请求的默认逻辑,无需使用@action,直接重写视图集的create方法即可:
from rest_framework.response import Response from rest_framework import status class LeadsViewSet( mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin, DestroyModelMixin, ): serializer_class = LeadSerializer queryset = Leads.objects.all() def create(self, request, *args, **kwargs): try: # 改用request.data解析请求数据,request.body是原始字节无法直接取值 date = request.data.get("date") user_id = request.data.get("user_id") unit_id = request.data.get("unit_id") if not all([user_id, unit_id]): raise serializers.ValidationError(_("user_id和unit_id为必填项")) existing_lead = self.queryset.filter(user_id=user_id, unit_id=unit_id).exists() if existing_lead: raise serializers.ValidationError(_("Lead already exists")) Leads.objects.create( user_id=user_id, unit_id=unit_id, date=date ) # 返回标准Response对象,不能直接返回字典 return Response({"message":"Lead Created Succesfully"}, status=status.HTTP_201_CREATED) except serializers.ValidationError as e: return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) except Exception as e: return Response({"error": _("Lead not created")}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
方法2:移除CreateModelMixin并调整自定义方法路径
如果希望保留@action定义的方法,先移除mixins.CreateModelMixin,再通过url_path参数将自定义方法绑定到根路径:
from rest_framework.response import Response from rest_framework import status class LeadsViewSet( mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet, # 移除CreateModelMixin,取消默认POST逻辑 mixins.UpdateModelMixin, DestroyModelMixin, ): serializer_class = LeadSerializer queryset = Leads.objects.all() @action(methods=["POST"], detail=False, url_path='') def create_lead(self, request, pk=None, *args, **kwargs): try: date = request.data.get("date") user_id = request.data.get("user_id") unit_id = request.data.get("unit_id") if not all([user_id, unit_id]): raise serializers.ValidationError(_("user_id和unit_id为必填项")) existing_lead = self.queryset.filter(user_id=user_id, unit_id=unit_id).exists() if existing_lead: raise serializers.ValidationError(_("Lead already exists")) Leads.objects.create( user_id=user_id, unit_id=unit_id, date=date ) return Response({"message":"Lead Created Succesfully"}, status=status.HTTP_201_CREATED) except serializers.ValidationError as e: return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) except Exception as e: return Response({"error": _("Lead not created")}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
额外注意事项
- 禁止使用
request.body.get,DRF会自动解析请求体数据到request.data中,应使用request.data.get取值。 - 自定义方法必须返回
Response对象,不能直接返回字典,否则会抛出异常。 - 区分异常类型处理,避免所有异常返回相同错误信息,便于排查问题。
内容的提问来源于stack exchange,提问作者maxwell morgenbesser
相关产品推荐
相关产品推荐

