首页 > 解决方案 > 姜戈休息。为嵌套对象列表创建自定义验证错误响应,必须返回无效值列表

问题描述

对不起,这太长了。
我用于创建 Item 对象的端点接收以下 json:

{
    "items_data": [
        {
            "item_id": 1,
            "item_name": "Foo",
            "item_quantity": 15
        },
        {
            "item_id": 2,
            "item_name": "Bar",
            "item_quantity": 100
        }
    ]
}

我需要从嵌套列表中创建几个对象,以防验证错误(在任何字段中),响应必须是未验证 ID 的列表(来自 item_id)。

例如,对于这样的请求(“item_id”和“item_quantity”中的字符串):

{
    "items_data": [
        {
            "item_id": "Baz",
            "item_name": "Bar",
            "item_quantity": 555
        },
        {
            "item_id": 17,
            "item_name": "FooBar",
            "item_quantity": "many"
        }
    ]
}

答案必须是这样的:

{
    "errors": 
        [
            {"id": "Baz"},
            {"id": 17}
        ]
}

为此,我重写了 ItemCreateSerializer 中的 to_internal_value 方法。如果出现验证错误,我会捕获异常,并引发一个新的 ValidationError 以添加有关失败验证的 id 的数据。

    def to_internal_value(self, data):
        try:
            ret = super().to_internal_value(data)
        except ValidationError:
            raise ValidationError({'id': data['item_id']})

        return ret

当我通过邮递员进行测试时,响应正文看起来不错。但是当我运行 django 测试时,它返回一个错误。

问题:
我做的是正确的事,还是可以做不同的事?
如何从响应中删除 ErrorDetail,只得到一个字符串?

测试错误:

self.assertEqual(response.data, {"errors": [{"id": "Baz"}, {"id": 17}]})

AssertionError: {'errors': [{'id': ErrorDetail(string='Baz', code='invalid')[48 chars]')}]} != {'errors': [{'id': 'Baz'}, {'id': 17}]}

我的观点:

class ItemsViewSet(viewsets.ModelViewSet):
    queryset = Item.objects.all()
    serializer_class = ItemDataSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            self.perform_create(serializer)
            return Response(
                serializer.data,
                status=status.HTTP_201_CREATED
            )
        print(serializer.errors)
        # Delete empty error dicts from list
        error_response = [error_id for error_id in
                          serializer.errors['items_data'] if error_id]
        return Response(
            {'errors': error_response},
            status=status.HTTP_400_BAD_REQUEST
        )

我的商品型号:

class Item(models.Model):

    id = models.PositiveIntegerField(primary_key=True)
    name = models.CharField(max_length=100)
    quantity = models.PositiveIntegerField(default=0)

模型和输入数据中的字段名称不同,所以我的序列化器:

class ItemCreateSerializer(serializers.ModelSerializer):
    item_id = serializers.IntegerField(
        min_value=1,
        write_only=True,
        source='id',
    )

    item_name = serializers.CharField(
        source='name',
        write_only=True,
    )

    item_quantity = serializers.IntegerField(
        required=True,
        min_value=1,
        write_only=True,
        source='quantity'
    )

    class Meta:
        model = Item
        fields = ('item_id', 'item_name', 'item_quantity', 'id')
        extra_kwargs = {
            'id': {'read_only': True},
        }

    def to_internal_value(self, data):
        try:
            ret = super().to_internal_value(data)
        except ValidationError as exc:
            print(exc)
            raise ValidationError({'id': data['item_id']})

        return ret


class ItemDataSerializer(serializers.Serializer):
    items_data = ItemCreateSerializer(many=True)

    class Meta:
        fields = ('items_data',)

    def create(self, validated_data):
        items = validated_data.pop('items_data')
        created_items = (Item.objects.create(**item_data) for item_data in items)
        return {'items_data': created_items}

标签: django-rest-framework

解决方案


推荐阅读