我有两个序列化程序:PostSerializer和PostImageSerializer,它们都继承了DRF ModelSerializer。PostImage模型通过相关的_name='photos'与Post链接。在
由于我希望序列化程序执行update,PostSerializer将重写ModelSerializer中的update()方法,如官方DRF文档中所述。在
class PostSerializer(serializers.ModelSerializer):
photos = PostImageSerializer(many=True)
class Meta:
model = Post
fields = ('title', 'content')
def update(self, instance, validated_data):
photos_data = validated_data.pop('photos')
for photo in photos_data:
PostImage.objects.create(post=instance, image=photo)
return super(PostSerializer, self).update(instance, validated_data)
class PostImageSerializer(serializer.ModelSerializer):
class Meta:
model = PostImage
fields = ('image', 'post')
我还定义了一个继承ModelViewSet的视图集。在
^{pr2}$最后,PostViewSet注册到DefaultRouter。(省略代码)
目标很简单。在
我得到400个响应,错误消息如下。在
{
"photos": [ "This field is required." ],
"title": [ "This field is required." ],
"content": [ "This field is required." ]
}
(请注意,错误消息可能与DRF错误消息不完全匹配,因为它们是经过翻译的。)
很明显,我的PUT字段都没有应用。 所以我一直在研究Django rest框架的源代码本身,并在ViewSetupdate() method continues to fail中发现了序列化程序验证。在
我对此表示怀疑,因为我不是通过JSON而是通过使用键值对的表单数据来提交请求请求数据未正确验证。在
但是,我应该在请求中包含多个图像,这意味着普通JSON无法工作。在
对于这个案子,最清楚的解决办法是什么?在
谢谢。在
正如Neil所指出的,我在PostSerializer的update()方法的第一行添加了print(self)。但是我的控制台上没有打印出来。在
我认为这是由于我上面的错误,因为调用serializer update()方法的perform_pdate()方法在serializer is validated之后被调用。在
因此,我的问题的主要概念可以缩小到以下几个方面。在
再次感谢。在
首先需要设置标题:
您不能将图像作为json数据发送(除非您将其编码为字符串并在服务器端解码为图像,例如base64)。在
在DRF中,PUT默认需要所有字段。如果只想设置部分字段,则需要使用PATCH。在
要解决此问题并使用PUT更新部分字段,您有两个选项:
您可以重写viewsetupdate方法以始终更新序列化程序部分(仅更改提供的字段):
^{pr2}$添加
休息_framework.parsers.MultiPartParser
到REST_FRAMEWORK dict的主设置文件:
看看你的序列化程序,奇怪的是你没有从后序列化程序中得到错误,因为你没有添加“photos”字段到元字段元组。在
在这种情况下,我有更多的建议:
答案是某种程度上的混合,或者是尼尔和蒙的答案。不过,我会再理顺一点。在
分析
现在邮递员提交包含2个键值对的表单数据(请参阅我在原始问题中上传的照片)。一个是与多个照片文件链接的“photos”键字段,另一个是“data”键字段,它与一个'JSON-like string'链接在一起。尽管这是一种将数据与文件一起发布或放置的公平方法,但DRF MultiPartParser或JSONParser无法正确解析这些文件。在
我收到错误消息的原因很简单。
self.get_serializer(instance, data=request.data, partial=partial
中的方法ModelViewSet
(尤其是UpdateModelMixin)无法理解request.data
部分。在当前来自提交表单数据的
request.data
如下所示。在仔细观察“请求”部分。该值是一个普通的
string
对象。在但是,我的PostSerializer希望
^{pr2}$request.data
看起来像下面这样。在因此,让我们做一些实验,并按照上面的JSON表单放置一些数据。 i、 e
您将收到如下错误消息。在
这意味着每个数据都被正确地提交,但是图像url http://tny.im/gMU不是一个文件而是一个字符串。在
现在这整个问题的原因变得很清楚了。序列化程序可以理解它需要提交的数据格式。在
解决方案
1。编写新的解析器
新的解析器应该将'JSON-like'字符串解析为正确的JSON数据。我从here.借用了MultipartJSONParser
这个解析器所做的很简单。如果我们提交带有键'data'的'JSON-like'字符串,请从rest\u框架调用
json
并对其进行解析。然后,返回解析后的JSON和请求的文件。在2。重新设计序列化程序
官方DRF文档建议嵌套序列化程序更新或创建相关对象。然而,我们有一个明显的缺点,InMemoryFileObject不能转换成序列化程序所期望的正确格式。为此,我们应该
update
方法request.data
弹出“照片”键值对request.data
中,密钥名为“photos”。这是因为我们的PostSerializer要求密钥名为“photos”。在然而,基本上
request.data
是一个默认情况下不可变的查询集。我很怀疑我们是否必须强制变异查询集。因此,我宁愿将后映像创建过程委托给ModelViewSet
的update()
方法。在这种情况下,我们不需要再定义了。在只需这样做:
3。从
ModelViewSet
重写update()
方法为了利用解析器类,我们需要显式地指定它。我们将整合PATCH和PUT行为,因此设置
partial=True
。正如我们前面看到的,图像文件带有键“photos”,因此弹出值并创建每个Photo实例。在最后,由于我们新设计的解析器,普通的“类JSON”字符串将被转换为常规JSON数据。所以只要简单地把每件事都放进}。在
serializer_class
和{结论
解决方案可行,但我不确定这是保存外键相关模型的最干净的方法。我强烈感觉应该是序列化程序保存相关模型。正如文件所述,除了文件以外的数据都是这样保存的。如果有人能告诉我更巧妙的方法,我将不胜感激。在
相关问题 更多 >
编程相关推荐