Django Rest Framework字段验证失效:触发数据库完整性错误而非JSON响应
问题原因分析
- 验证方法命名错误:序列化器中定义的
validate_subscribing方法不会被DRF调用——DRF的字段级验证方法命名规则是validate_<字段名>,而你的序列化器字段是author,subscribing只是模型中author字段的related_name,这直接导致自订阅的验证逻辑完全没执行。 - 验证逻辑存在漏洞:即便方法名正确,当前代码里
self.context.get('request').user == value的比较也不成立——value是传入的author ID(视图中传入的是author.id),而request.user是User对象,对象与ID无法直接相等,验证条件永远触发不了。 - 未处理数据库约束异常:序列化器验证失效后,数据库的CheckConstraint会拦截操作,但你没有在视图或序列化器中捕获
IntegrityError,导致直接抛出500错误而非返回JSON格式的错误响应。
解决方法
1. 修复序列化器的验证逻辑
修改SubscriptionSerializer中的验证方法,确保方法名正确且逻辑有效:
class SubscriptionSerializer(serializers.ModelSerializer): user = serializers.PrimaryKeyRelatedField( read_only=True, default=serializers.CurrentUserDefault()) class Meta: model = models.Subscription fields = ('author', 'user', ) validators = [ serializers.UniqueTogetherValidator( queryset=models.Subscription.objects.all(), fields=['author', 'user', ] ) ] def create(self, validated_data): return models.Subscription.objects.create( user=self.context.get('request').user, **validated_data) # 修正方法名为validate_author,对应序列化器的author字段 def validate_author(self, value): current_user = self.context.get('request').user # value是DRF自动转换后的User对象,直接比较ID if current_user.id == value.id: raise serializers.ValidationError('不能订阅自己!') return value
2. 在视图中捕获数据库异常(推荐)
为处理并发场景下可能出现的约束冲突(比如同一用户同时发起两次订阅请求),在视图的subscribe方法中添加异常捕获:
from django.db import IntegrityError @action(['post'], detail=True) @permission_classes(permissions.IsAuthenticated) def subscribe(self, request, *args, **kwargs): author = get_object_or_404(models.User, id=kwargs['id']) data = request.data.copy() data.update({'author': author.id}) serializer = serializers.SubscriptionSerializer( data=data, context={'request': request}) if request.method == 'POST': try: serializer.is_valid(raise_exception=True) serializer.save() return Response( status=status.HTTP_201_CREATED, data=self.get_serializer(author).data) except IntegrityError as e: if 'prevent_self_subscription' in str(e): return Response( {'error': '不能订阅自己!'}, status=status.HTTP_400_BAD_REQUEST ) elif 'unique_subscription' in str(e): return Response( {'error': '你已经订阅过该用户!'}, status=status.HTTP_400_BAD_REQUEST ) # 其他未知异常正常抛出 raise
3. 优化视图逻辑(可选)
视图中已经通过get_object_or_404获取了author对象,无需再传入ID,直接将对象传入序列化器,减少转换步骤:
@action(['post'], detail=True) @permission_classes(permissions.IsAuthenticated) def subscribe(self, request, *args, **kwargs): author = get_object_or_404(models.User, id=kwargs['id']) # 直接传入author对象,无需处理ID serializer = serializers.SubscriptionSerializer( data={'author': author}, context={'request': request}) if request.method == 'POST': try: serializer.is_valid(raise_exception=True) serializer.save() return Response( status=status.HTTP_201_CREATED, data=self.get_serializer(author).data) except IntegrityError as e: # 异常处理逻辑同上 ...
内容的提问来源于stack exchange,提问作者PETRUSHQUE IVANOFF
相关产品推荐
相关产品推荐

