在Django-rest-framework中解析多部分/表单数据 [英] Parsing multipart/form-data in django-rest-framework

查看:75
本文介绍了在Django-rest-framework中解析多部分/表单数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用django-rest-framework时,在解析多部分表单数据时遇到一些困难。我设置了一个最小的视图以仅回显请求数据:



from rest_framework。视图从rest_framework.response导入APIView
.response导入从rest_framework.parsers导入响应
multiFormParser,FormParser


class FileUpload(APIView):
parser_classes =( MultiPartParser,FormParser,)

def post(self,request,format = None,* args,** kwargs):
return Response({'raw':request.data,'data ':request._request.POST,
文件:str(request._request.FILES)})

我希望 raw (我承认这个名字有点不好)实际上包含与 request._request.POST request._request.FILES



如果我 POST 使用 Content-Type = application / x-www查看-form-urlencoded 可以按预期工作:

  $ http -v --form POST http: // localhost:8000 / upload / api / course = 3 name = name 

POST / upload / api / HTTP / 1.1
Accept:* / *
Accept-Encoding: gzip,deflate
连接:keep-alive
内容长度:18
内容类型:application / x-www-form-urlencoded; charset = utf-8
主机:localhost:8000
用户代理:HTTPie / 0.9.2

course = 3& name = name

HTTP / 1.0 200 OK
允许:开机自检,选项
内容类型:application / json
日期:2015年12月17日星期四16:52:37 GMT
服务器:WSGIServer / 0.2 CPython / 3.4.3
变化:接受,Cookie
X-Frame-Options:SAMEORIGIN

{
data:{
当然: 3,
name:名称
},
files:< MultiValueDict:{}>,
raw: {
course: 3,
name: name
}
}

但是,如果我使用 Content-Type = multipart / form-data 发布,则会得到以下信息:

  $ http -v --form POST http:// localhost:8000 / upload / api / file@~/Projects/lms/manage.py course = 3 name = name 
POST / upload / api / HTTP / 1.1
接受:* / *
接受编码:gzip,压缩def
连接:keep-alive
内容长度:577
内容类型:multipart / fo rm-data; boundary = 634ec7c7e89a487b89c1c07c0d24744c
主机:localhost:8000
用户代理:HTTPie / 0.9.2

--634ec7c7e89a487b89c1c07c0d24744c
内容处置:表单数据; name = course

3
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition:表单数据; name = name

name
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition:表单数据; name =文件; filename = manage.py

#!/ usr / bin / env python
import os
import sys

如果__name__ == __main__ :
os.environ.setdefault( DJANGO_SETTINGS_MODULE, config.settings.local)

来自django.core.management导入execute_from_command_line

execute_from_command_line( sys.argv)

--634ec7c7e89a487b89c1c07c0d24744c--

HTTP / 1.0 200 OK
允许:POST,OPTIONS
内容类型:application / json
日期:2015年12月17日星期四16:55:44 GMT
服务器:WSGIServer / 0.2 CPython / 3.4.3
变化:接受,Cookie
X-Frame-Options: SAMEORIGIN

{
data:{
course: 3,
name: name
},
files:< MultiValueDict:{'file':[< InMemoryUploadedFile:manage.py()>]}>,
raw:{}
}

我在这里错过了什么吗?我正在使用HTTPIE在此处生成请求,但是curl仍然存在相同的行为,因此我可以肯定这不是问题。我正在使用djangorestframework == 3.3.0和Django == 1.8.4



编辑:



PUT 指向url(具有相同的请求)可以达到预期的结果:

  $ http -v --form PUT http:// localhost:8000 / upload / api / file@~/Projects/lms/manage.py course = 3 name = name 
PUT / upload / api / HTTP / 1.1
接受:* / *
接受编码:gzip,放气
连接:keep-alive
内容长度:577
内容类型:multipart / form-data; boundary = 98feb59a8abe4bfb95a7321f536ed800
主机:localhost:8000
User-Agent:HTTPie / 0.9.2

--98feb59a8abe4bfb95a7321f536ed800
内容处置:表单数据; name = course

3
--98feb59a8abe4bfb95a7321f536ed800
内容处置:表格数据; name = name

name
--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition:表单数据; name =文件; filename = manage.py

#!/ usr / bin / env python
import os
import sys

如果__name__ == __main__ :
os.environ.setdefault( DJANGO_SETTINGS_MODULE, config.settings.local)

来自django.core.management导入execute_from_command_line

execute_from_command_line( sys.argv)

--98feb59a8abe4bfb95a7321f536ed800--

HTTP / 1.0 200 OK
允许:POST,PUT,OPTIONS
内容类型:application / json
日期:2015年12月17日,星期四18:10:34 GMT
服务器:WSGIServer / 0.2 CPython / 3.4.3
变化:接受,Cookie
X-Frame-选项:SAMEORIGIN

{
data:{},
files:< MultiValueDict:{}>,
raw: < QueryDict:{'名称':['名称'],'课程':['3'],'文件':[< InMemoryUploadedFile:manage.py()>]}}>
}

所以我只能使用 PUT 。但是,这并不理想,因为客户端无法控制文件的名称或文件在服务器上的位置。在这种情况下, POST 更合适。无论如何,我不明白为什么 PUT POST 不起作用的情况下起作用。

解决方案

这是您使用的版本的已知问题。将django rest框架升级到最新版本将解决此问题。但是,您可以将请求作为解决方法。


I'm having some difficulties parsing multipart form data when using the django-rest-framework. I've set up a minimal view to just echo back the request data:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser


class FileUpload(APIView):
    parser_classes = (MultiPartParser, FormParser, )

    def post(self, request, format=None, *args, **kwargs):
        return Response({'raw': request.data, 'data': request._request.POST,
                         'files': str(request._request.FILES)})

I am expecting that raw (slightly badly named I admit) contains effectively the same data as request._request.POST and request._request.FILES.

If I POST to the view with Content-Type= application/x-www-form-urlencoded this works as expected:

$ http -v --form POST http://localhost:8000/upload/api/ course=3 name=name

POST /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 18
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:8000
User-Agent: HTTPie/0.9.2

course=3&name=name

HTTP/1.0 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 16:52:37 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {
        "course": "3",
        "name": "name"
    },
    "files": "<MultiValueDict: {}>",
    "raw": {
        "course": "3",
        "name": "name"
    }
}

However if I post with Content-Type=multipart/form-data I get the following:

$ http -v --form POST http://localhost:8000/upload/api/ file@~/Projects/lms/manage.py course=3 name=name
POST /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 577
Content-Type: multipart/form-data; boundary=634ec7c7e89a487b89c1c07c0d24744c
Host: localhost:8000
User-Agent: HTTPie/0.9.2

--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="course"

3
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="name"

name
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="file"; filename="manage.py"

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

--634ec7c7e89a487b89c1c07c0d24744c--

HTTP/1.0 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 16:55:44 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {
        "course": "3",
        "name": "name"
    },
    "files": "<MultiValueDict: {'file': [<InMemoryUploadedFile: manage.py ()>]}>",
    "raw": {}
}

Am I missing something here? I am using HTTPIE to generate the requests here but the same behaviour exists with curl so I'm pretty sure that is not the problem. I am using djangorestframework==3.3.0 and Django==1.8.4

EDIT:

It seems that PUTing to the url (with an otherwise identical request) achieves the desired result:

$ http -v --form PUT http://localhost:8000/upload/api/ file@~/Projects/lms/manage.py course=3 name=name
PUT /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 577
Content-Type: multipart/form-data; boundary=98feb59a8abe4bfb95a7321f536ed800
Host: localhost:8000
User-Agent: HTTPie/0.9.2

--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="course"

3
--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="name"

name
--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="file"; filename="manage.py"

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

--98feb59a8abe4bfb95a7321f536ed800--

HTTP/1.0 200 OK
Allow: POST, PUT, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 18:10:34 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {},
    "files": "<MultiValueDict: {}>",
    "raw": "<QueryDict: {'name': ['name'], 'course': ['3'], 'file': [<InMemoryUploadedFile: manage.py ()>]}>"
 }

So I could just use PUT. That is however not ideal as the client does not control what the file is named or where it is located on the server. POST is more appropriate in that sense. In any sense, I don't see why PUT works when POST doesn't.

解决方案

It is a known issue for the version you're using. Upgrading django rest framework to the latest version will solve the problem. However, you can PUT the request as a workaround.

这篇关于在Django-rest-framework中解析多部分/表单数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆