我正在尝试使用Django Rest Framework进行 PATCH 请求,但收到以下错误:
{"image_data": [{"non_field_errors": ["Invalid data"]}]
我知道JSONField()可以提出一些问题,所以我通过添加to_native
和from_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”:[“无效数据”]}]
答案 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}}
希望这有帮助。