Pro*_*eus 5 python django django-rest-framework
我正在尝试使用Django Rest Framework 进行PATCH请求,但是收到以下错误:
{"image_data": [{"non_field_errors": ["Invalid data"]}]
Run Code Online (Sandbox Code Playgroud)
我知道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()
Run Code Online (Sandbox Code Playgroud)
串行器:
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")
Run Code Online (Sandbox Code Playgroud)
补丁:
> payload = {"image_data": {"thisdata": "{}"}}
> requests.patch("/photo/123/",payload )
Run Code Online (Sandbox Code Playgroud)
我也尝试过:
> payload = {"image_data": [{"thisdata": "{}"}]}
> requests.patch("/photo/123/",payload )
Run Code Online (Sandbox Code Playgroud)
但再次给出同样的错误:
[{"non_field_errors":["无效数据"]}]
Django Rest Framework关系序列化的最初想法是不改变相关领域的价值.这意味着,你的有效载荷应包含pk的PhotoData对象,而不是它的数据集.就像在模型中一样,你不能将dict分配给外键字段.
好(仅适用于自身包含问题的serializers.PrimaryKeyRelatedField):
payload = {"image_data": 2}
Run Code Online (Sandbox Code Playgroud)
错误(默认情况下不适用于DRF):
payload = {"image_data": {'thisdata': '{}'}}
Run Code Online (Sandbox Code Playgroud)
实际上你提供的数据模型根本不需要PhotoData(你可以将thisdata字段移动到Photo),但是假设你有一个特殊情况,即使是Python的Zen说Special cases aren't special enough to break the rules..
所以,这里有一些可能的方法:
使用字段序列化程序(您的原始方式)
你现在想做什么是可能的,但是非常难看的解决方案.您可以创建一个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)
Run Code Online (Sandbox Code Playgroud)
并使用它 PhotoSerializer
class PhotoSerializer(serializers.ModelSerializer):
image_data = PhotoDataField(read_only=False, source='image_data')
class Meta:
model = Photo
fields = ("id", "user", "image_data")
Run Code Online (Sandbox Code Playgroud)
所以请求会很好
payload = {"image_data": '{}'}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/"), payload)
Run Code Online (Sandbox Code Playgroud)
和"好"的要求也
photodata = PhotoData.objects.get(pk=1)
payload = {"image_data": photodata.pk}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/"), payload)
Run Code Online (Sandbox Code Playgroud)
结果你会在GET请求中看到"image_data": <photodata's thisdata value>,.
但是,即使您将通过这种方法修复验证问题,从我的代码中可以看出它仍然是一个巨大的痛苦(当您想要"打破正常的工作流程"时,这只是DRF可以提供给您的东西,Tastypie提供更多).
规范化您的代码并使用@action(推荐)
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")
Run Code Online (Sandbox Code Playgroud)
现在在api的视图中定义一个特定的方法,您可以使用它来设置任何照片的数据
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)
Run Code Online (Sandbox Code Playgroud)
现在,您可以执行与现在几乎相同的请求,但您在代码中具有更多可扩展性和职责划分. 查看URL,当你有@ action的包装方法时,它会被附加.
payload = {"thisdata": '{"test": "ok"}'}
resp = requests.patch(request.build_absolute_uri("/api/photo/1/set_photodata/"), payload)
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.
| 归档时间: |
|
| 查看次数: |
956 次 |
| 最近记录: |