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

DRF无请求体POST创建Subscription失败问题求助

问题排查与修复方案

核心问题分析

  1. SubscriptionCreateSerializer无效:该序列化器的create方法仅写了pass,调用save()时无法生成数据库记录;同时设置fields='__all__',但请求无请求体,验证阶段会缺失必填字段,且代码未触发验证异常,后续逻辑强行执行导致报错。
  2. 未实现订阅去重:未检查用户是否已订阅目标作者,可能导致重复创建订阅记录。
  3. 未返回指定序列化数据:原代码仅返回状态码,未按要求返回SubscriptionSerializer序列化后的内容。
  4. 删除逻辑缺失:原DELETE分支仅返回状态码,未实际删除对应订阅记录。

修复代码

1. 修复SubscriptionCreateSerializer

该序列化器仅用于创建订阅,无需接收请求体字段,调整如下:

class SubscriptionCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscription
        fields = []  # 无需请求体数据,user和author由视图传入

    def create(self, validated_data):
        # 实际执行订阅记录创建
        return Subscription.objects.create(**validated_data)

2. 修复CustomUserViewSet的subscribe方法

补充删除逻辑、订阅去重检查,并返回SubscriptionSerializer序列化数据:

class CustomUserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = CreateUserSerializer

    @action(
        detail=True,
        methods=['post', 'delete'],
        permission_classes=[IsAuthenticated],
    )
    def subscribe(self, request, *args, **kwargs):
        user = request.user
        author_pk = int(kwargs.get('pk'))
        author_to_subscribe = get_object_or_404(User, id=author_pk)

        if request.method == 'DELETE':
            # 实际删除对应订阅记录
            Subscription.objects.filter(user=user, author=author_to_subscribe).delete()
            return Response(status=status.HTTP_204_NO_CONTENT)

        if user.pk == author_pk:
            return Response(
                data={"errors": "Cannot subscribe to yourself"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 检查是否已订阅,避免重复创建
        if Subscription.objects.filter(user=user, author=author_to_subscribe).exists():
            return Response(
                data={"errors": "Already subscribed to this author"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 验证并创建订阅
        serializer = SubscriptionCreateSerializer(data={})
        serializer.is_valid(raise_exception=True)
        subscription = serializer.save(user=user, author=author_to_subscribe)

        # 使用SubscriptionSerializer序列化并返回结果
        response_serializer = SubscriptionSerializer(subscription, context={'request': request})
        return Response(
            data=response_serializer.data,
            status=status.HTTP_201_CREATED  # 创建资源使用201状态码更符合REST规范
        )

3. 优化SubscriptionSerializer

原序列化器中的to_internal_value方法用于反序列化(处理请求数据),但该序列化器仅用于返回响应数据,删除该方法避免潜在报错:

class SubscriptionSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(source='author.email')
    id = serializers.IntegerField(source='author.id')
    username = serializers.CharField(source='author.username')
    first_name = serializers.CharField(source='author.first_name')
    last_name = serializers.CharField(source='author.last_name')
    is_subscribed = serializers.BooleanField(default=True)
    recipes = serializers.SerializerMethodField()

    def get_recipes(self, obj):
        qs = obj.author.recipes.all()
        return RecipePublicSerializer(qs, many=True).data

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['recipes_count'] = instance.author.recipes.count()
        return representation

    class Meta:
        model = Subscription
        fields = ('email',
                  'id',
                  'username',
                  'first_name',
                  'last_name',
                  'is_subscribed',
                  'recipes',)

内容的提问来源于stack exchange,提问作者aleksander

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:31:32