DRF对象级validate函数失效,抛出TypeError而非预期ValidationError
问题:DRF自定义validate方法校验失败时触发TypeError而非预期ValidationError
在基于Django REST Framework开发接口时,为自定义的IdentitySerializer实现了对象级validate方法,用于校验firstName与lastName不能相同。但发送包含相同firstName和lastName的POST请求时,未抛出预期的ValidationError(提示"first name last name cannot be same"),反而触发TypeError,错误信息如下:
"C:\Python310\lib\site-packages\rest_framework\serializers.py", line 204, in save validated_data = {**self.validated_data, **kwargs} TypeError: 'ValidationError' object is not a mapping
相关代码及请求体如下:
views.py
from rest_framework import generics from .models import Identity from .serializers import IdentitySerializer from rest_framework.response import Response from rest_framework import status from rest_framework.decorators import api_view @api_view(['GET']) def IdentityListApiView(request): queryset = Identity.objects.all() serializer = IdentitySerializer(queryset, many=True) return Response(serializer.data) @api_view(['POST']) def IdentityDetailApiView(request): serializer = IdentitySerializer(data=request.data) print("isData valid:", serializer.is_valid()) if serializer.is_valid(raise_exception=True): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED)
serializers.py
from rest_framework import serializers from .models import Identity class IdentitySerializer(serializers.Serializer): firstName=serializers.CharField(max_length=40) lastName=serializers.CharField(max_length=40) age=serializers.IntegerField(max_value=None, min_value=None) mobileNo=serializers.IntegerField(read_only=False) isMale=serializers.BooleanField(read_only=False) def create(self, validated_data): return Identity.objects.create(**validated_data) def validate(self, data): if data['firstName'] == data['lastName']: return serializers.ValidationError('first name last name cannot be same') return data
models.py
from django.db import models class Identity(models.Model): firstName = models.CharField(max_length=40, null=False, blank=False, default='M') lastName = models.CharField(max_length=40, null=False, blank=False, default='K') age = models.IntegerField(null=True) mobileNo = models.IntegerField(null=True) isMale = models.BooleanField(blank=True, null=True)
请求体
{ "firstName": "abc", "lastName": "abc", "age": "56", "mobileNo":" 589643145", "isMale": true }
解决方法
错误原因
DRF的validate方法规则:验证通过时返回校验后的data字典;验证失败时必须抛出ValidationError异常,而非返回该异常对象。当前代码错误地将ValidationError对象作为返回值,导致self.validated_data被赋值为异常对象,后续serializer.save()执行时试图将其当作字典解构,因此触发TypeError。
修正后的serializers.py代码
修改validate方法,将return serializers.ValidationError(...)改为raise serializers.ValidationError(...):
from rest_framework import serializers from .models import Identity class IdentitySerializer(serializers.Serializer): firstName=serializers.CharField(max_length=40) lastName=serializers.CharField(max_length=40) age=serializers.IntegerField(max_value=None, min_value=None) mobileNo=serializers.IntegerField(read_only=False) isMale=serializers.BooleanField(read_only=False) def create(self, validated_data): return Identity.objects.create(**validated_data) def validate(self, data): if data['firstName'] == data['lastName']: # 改为抛出异常,而非返回 raise serializers.ValidationError('first name last name cannot be same') return data
验证效果
修改后,发送相同POST请求时,DRF会自动捕获ValidationError,返回包含错误信息的400响应,符合预期校验逻辑。
内容的提问来源于stack exchange,提问作者KumarZ
相关产品推荐
相关产品推荐

