使用嵌套可写序列化程序的django rest框架文件上载

时间:2014-08-13 16:29:34

标签: python django django-rest-framework

class AnnotationSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Annotation


class ImageSerializer(serializers.HyperlinkedModelSerializer):
    annotations = AnnotationSerializer(many=True, required=False)

    class Meta:
        depth = 1
        model = Image
        exclude = ('owner‘,)

注释具有图像外键属性,因此图像可能具有多个注释。我想通过对图像端点的发布请求创建带有嵌套注释的图像,包括该图像的注释列表。将我的数据json编码发送到图像端点确实有效并创建带有适当注释的图像。

但是当我尝试上传实际图像时,我必须使用多部分/表单编码的post请求而不是json,以使fileupload成为可能。现在我很难获得此请求中包含的嵌套图像注释列表。也许我可以将一个json编码的字符串放在某个表单字段中并在视图中手动解析它,覆盖request.DATA,但这看起来真的很难看。

我想知道是否有更好的方法来实现我想要做的事情:)。

3 个答案:

答案 0 :(得分:6)

我发现解决此问题的最好方法是编写一个自定义解析器来解析传入的多部分请求。我一直在使用formencode进行实际解析,但您可以使用任何嵌套的formdata库。这需要很少的代码:

from rest_framework import parsers
from formencode.variabledecode import variable_decode

class MultipartFormencodeParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = variable_decode(result.data)
        return parsers.DataAndFiles(data, result.files)

然后在ViewSet

class ImageViewSet(viewsets.ModelViewSet):
    ...
    parsers = (MultipartFormencodeParser,)
    ...

Formencode将列表编码为编码表单数据中的<key>-<index>条目,将嵌套属性表示为<item>.<proprety>。因此,在您的示例中,客户端需要将注释编码为请求中的"annotation-1.name"。显然,您仍需要在序列化程序中手动处理嵌套数据的更新,如其余框架文档中所述here

答案 1 :(得分:1)

您是什么意思在请求中包含难以获取嵌套的图像注释列表?发送multpart/form-data发布请求时,request.data中是否包含嵌套表示法列表数据? (请使用request.data代替request.DATArequest.FILES)。请使用pdb等一些调试工具来检查request.data

为了支持可写嵌套序列化程序,我认为你应该覆盖create()方法的POST函数,你可以从here找到更多细节。很抱歉没有直接给出答案,我需要有关您的模型的更多详细信息。

如果您想发布JSON而不是multipart/form-data,可以使用base64来表示数字(但文件的大小将增加近33%)。

答案 2 :(得分:1)

你可以使用formencode.variabledecode.variable_decode()

示例:

class ImageSerializer(serializers.HyperlinkedModelSerializer):

    ...

    def to_internal_value(self, value):
        return super(ImageSerializer, self).to_internal_value(variable_decode(value))