Django Rest Framework不像在标准POST请求中那样解码多部分请求中的JSON字段

时间:2016-08-05 21:54:09

标签: python json django django-rest-framework

当我提交Content-Type application/json的POST请求时,我在服务器上的数据会被解码为本机Python - JSON对象显示为dicts,数组为阵列等等。这很棒 但是,当我对我的API执行MultiPart发布请求时,当然也包含一个文件,任何包含JSON /对象的字段都不会在服务器上解码,而我还需要解码我需要解码的字符串我。我的应用程序的性质意味着我无法总是知道我将要获得哪些字段。

我的问题是 - 如何使用文件提交多部分请求,同时保留DRF在某些字段中解码JSON数据的能力? 我已尝试将所有3个主要解析器一起使用,但这不起作用(通过将API视图的parser_classes设置为它们: parser_classes = (MultiPartParser, JSONParser, FormParser)

以下是一些示例请求(通过Chrome浏览器的开发工具):

标准发布(不是多部分,没有文件): {"first_name":"Test","last_name":"Shmest","email":[{"channel_type":"Email","value":"test@example.com","name":null,"default":false}],"company":{"position":"Manager","id":"735d2b5f-e032-4ca8-93e4-c7773872d0cc","name":"The Compapa"},"access":{"private":true,"users":[10,1]},"description":"Nice guy!!","address":{"city":"San Fanfanfo","zip":"39292","country":"United States of America","state":"CA","map_url":null,"country_code":"US","address":"123 This street"},"phone":[{"default":false,"type":"Phone","id":"70e2b437-6841-4536-9acf-f6a55cc372f6","value":"+141512312345","name":null}],"position":"","department":"","supervisor":"","assistant":"","referred_by":"","status":"","source":"","category":"Lead","do_not_call":false,"do_not_text":false,"do_not_email":false,"birthday":null,"identifier":""} DRF可以很好地读取此有效负载,并将所有值设置为其本机等效值。

Multipart with file,其中一个字段是JSON编码对象:

------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="file"; filename="image.png"
Content-Type: image/png


------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="first_name"

Test
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="last_name"

Shmest
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="email"

[{"channel_type":"Email","value":"test@example.com","name":null,"default":false}]
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="company"

{"position":"Manager","id":"735d2b5f-e032-4ca8-93e4-c7773872d0cc","name":"The Compapa"}
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="access"

{"private":true,"users":[10,1]}
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="description"

Nice guy!!
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb
Content-Disposition: form-data; name="address"

我正在寻找的是在我深入手动解码所有字段以检查它们是否为JSON之前,看看是否有某种方法让JSON解码在多部分请求中是自动的,就像它在常规POST中一样或不。在我发出请求之前,我所知道的大多数字段都是未知的,因为每个用户可能有不同的字段组合。

1 个答案:

答案 0 :(得分:1)

我创建了一个新的Parser对象来处理包含JSON的字段的MultiPart文件上传方案。如果有人需要,代码如下。

import json
from rest_framework.parsers import BaseParser, DataAndFiles
from django.conf import settings
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser, MultiPartParserError
from django.utils import six
from rest_framework.exceptions import ParseError


class MultiPartJSONParser(BaseParser):
    """
    Parser for multipart form data which might contain JSON values
    in some fields as well as file data.
    This is a variation of MultiPartJSONParser, which goes through submitted fields
    and attempts to decode them as JSON where a value exists. It is not to be used as a replacement
    for MultiPartParser, only in cases where MultiPart AND JSON data are expected.
    """
    media_type = 'multipart/form-data'

    def parse(self, stream, media_type=None, parser_context=None):
        """
        Parses the incoming bytestream as a multipart encoded form,
        and returns a DataAndFiles object.
        `.data` will be a `QueryDict` containing all the form parameters, and JSON decoded where available.
        `.files` will be a `QueryDict` containing all the form files.
        """
        parser_context = parser_context or {}
        request = parser_context['request']
        encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
        meta = request.META.copy()
        meta['CONTENT_TYPE'] = media_type
        upload_handlers = request.upload_handlers

        try:
            parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
            data, files = parser.parse()
            for key in data:
                if data[key]:
                    try:
                        data[key] = json.loads(data[key])
                    except ValueError:
                        pass
            return DataAndFiles(data, files)
        except MultiPartParserError as exc:
            raise ParseError('Multipart form parse error - %s' % six.text_type(exc))

解析器可以像API中一样在API视图中使用:

parser_classes = (MultiPartJSONParser, JSONParser , FormParser)