使用JSONField获取non_field_errors()

时间:2014-08-21 14:43:35

标签: python django django-rest-framework

我正在尝试使用Django Rest Framework进行 PATCH 请求,但收到以下错误:

{"image_data": [{"non_field_errors": ["Invalid data"]}]

我知道JSONField()可以提出一些问题,所以我通过添加to_nativefrom_native来解决这个问题,但是,我仍然遇到了这个问题。我认为JSONField()根本不是问题,但仍值得一提。

我相信我在尝试更新相关领域方面做了一些根本性的错误。

以下代码......

型号:

class Photo(models.Model):
    user = models.ForeignKey(AppUser, help_text="Item belongs to.")
    image_data = models.ForeignKey("PhotoData", null=True, blank=True)


class PhotoData(models.Model):
    thisdata = JSONField()

串行器:

class ExternalJSONField(serializers.WritableField):
    def to_native(self, obj):
        return json.dumps(obj)

    def from_native(self, value):
        try:
            val = json.loads(value)
        except TypeError:
            raise serializers.ValidationError(
                "Could not load json <{}>".format(value)
            )
        return val

class PhotoDataSerializer(serializers.ModelSerializer):

    thisdata = ExternalJSONField()
    class Meta:
        model = PhotoData
        fields = ("id", "thisdata")


class PhotoSerializer(serializers.ModelSerializer):

    image_data = PhotoDataSerializer()

    class Meta:
        model = Photo
        fields = ("id","user", "image_data")

PATCH:

> payload = {"image_data": {"thisdata": "{}"}}
> requests.patch("/photo/123/",payload )

我也尝试过:

> payload = {"image_data": [{"thisdata": "{}"}]}
> requests.patch("/photo/123/",payload )

但同样给出同样的错误:

[{“non_field_errors”:[“无效数据”]}]

1 个答案:

答案 0 :(得分:5)

Django Rest Framework关系序列化的最初想法是不改变相关字段的值。 这意味着您的有效负载应包含pk PhotoData payload = {"image_data": 2} 对象,而不是其中的数据集。 在模型中,您可以将dict分配给外键字段。

好(仅适用于自身包含问题的serializers.PrimaryKeyRelatedField):

   payload = {"image_data": {'thisdata': '{}'}}

错误(默认情况下不适用于DRF):

PhotoData

实际上,您提供的数据模型根本不需要thisdata(您可以将Photo字段移至Special cases aren't special enough to break the rules.),但我们假设您拥有特殊情况,即使Python的Zen说PhotoDataField

所以,这里有一些可能的方法:

使用字段序列化程序(您的原始方式)

你现在想做什么是可能的,但是非常难看的解决方案。 您可以创建class PhotoDataField(serializers.PrimaryKeyRelatedField): def field_to_native(self, *args): """ Use field_to_native from RelatedField for correct `to_native` result """ return super(serializers.RelatedField, self).field_to_native(*args) # Prepare value to output def to_native(self, obj): if isinstance(obj, PhotoData): return obj.thisdata return super(PhotoDataField, self).to_native(obj) # Handle input value def field_from_native(self, data, files, field_name, into): try: int(data['image_data']) except ValueError: # Looks like we have a data for `thisdata` field here. # So let's do write this to PhotoData model right now. # Why? Because you can't do anything with `image_data` in further. if not self.root.object.image_data: # Create a new `PhotoData` instance and use it. self.root.object.image_data = PhotoData.objects.create() self.root.object.image_data.thisdata = data['image_data'] self.root.object.image_data.save() return data['image_data'] except KeyError: pass # So native behaviour works (e.g. via web GUI) return super(PhotoDataField, self).field_from_native(data, files, field_name, into) 适用于我,但尚未准备好使用代码,仅用于演示

PhotoSerializer

并在class PhotoSerializer(serializers.ModelSerializer): image_data = PhotoDataField(read_only=False, source='image_data') class Meta: model = Photo fields = ("id", "user", "image_data")

中使用它
payload = {"image_data": '{}'}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/"), payload)

所以请求会运行良好

photodata = PhotoData.objects.get(pk=1)
payload = {"image_data": photodata.pk}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/"), payload)

和&#34;好&#34;请求

"image_data": <photodata's thisdata value>,

结果您将在GET请求Tastypie中看到。

但是,即使你用这种方法修复验证问题,你仍然可以从我的代码中看到屁股的巨大痛苦(这只是DRF可以在你想要的时候提供给你的东西&#34;打破一个正常的工作流程&#34;,class PhotoDataSerializer(serializers.ModelSerializer): class Meta: model = PhotoData fields = ("id", "thisdata") class PhotoSerializer(serializers.ModelSerializer): image_data = PhotoDataSerializer() # or serializers.RelatedField class Meta: model = Photo fields = ("id", "user", "image_data", "test") 提供更多)。

规范化您的代码并使用@action(推荐)

from rest_framework import viewsets, routers, generics
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status

# ViewSets define the view behavior.


class PhotoViewSet(viewsets.ModelViewSet):
    model = Photo
    serializer_class = PhotoSerializer

    @action(methods=['PATCH'])
    def set_photodata(self, request, pk=None):
        photo = self.get_object()
        serializer = PhotoDataSerializer(data=request.DATA)
        if serializer.is_valid():
            if not photo.image_data:
                photo.image_data = PhotoData.objects.create()
                photo.save()
            photo.image_data.thisdata = serializer.data
            photo.image_data.save()
            return Response({'status': 'ok'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

现在在您的api的视图中定义一个特定方法 ,您可以使用该方法设置任何照片的数据

payload = {"thisdata": '{"test": "ok"}'}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/set_photodata/"), payload)

现在您可以执行与现在几乎相同的请求,但您在代码中具有更多可扩展性和职责分工。 查看网址,如果您拥有@ action的包装方法,则会附加

{{1}}

希望这有帮助。