You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Rest Framework字段验证失效:触发数据库完整性错误而非JSON响应

问题原因分析
  1. 验证方法命名错误:序列化器中定义的validate_subscribing方法不会被DRF调用——DRF的字段级验证方法命名规则是validate_<字段名>,而你的序列化器字段是author,subscribing只是模型中author字段的related_name,这直接导致自订阅的验证逻辑完全没执行。
  2. 验证逻辑存在漏洞:即便方法名正确,当前代码里self.context.get('request').user == value的比较也不成立——value是传入的author ID(视图中传入的是author.id),而request.user是User对象,对象与ID无法直接相等,验证条件永远触发不了。
  3. 未处理数据库约束异常:序列化器验证失效后,数据库的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:31:00