Im trying to send some data and file using Python requests module to my django rest application but get the below error.
raise MultiPartParserError('Invalid boundary in multipart: %s' % boundary)
MultiPartParserError: Invalid boundary in multipart: None
Code:-
import requests
payload={'admins':[
{'first_name':'john'
,'last_name':'white'
,'job_title':'CEO'
,'email':'test1@gmail.com'
},
{'first_name':'lisa'
,'last_name':'markel'
,'job_title':'CEO'
,'email':'test2@gmail.com'
}
],
'company-detail':{'description':'We are a renowned engineering company'
,'size':'1-10'
,'industry':'Engineering'
,'url':'http://try.com'
,'logo':''
,'addr1':'1280 wick ter'
,'addr2':'1600'
,'city':'rkville'
,'state':'md'
,'zip_cd':'12000'
,'phone_number_1':'408-393-254'
,'phone_number_2':'408-393-221'
,'company_name':'GOOGLE'}
}
files = {'upload_file':open('./test.py','rb')}
import json
headers = {'content-type' : 'application/json'}
headers = {'content-type' : 'multipart/form-data'}
#r = requests.post('http://127.0.0.1:8080/api/create-company-profile/',data=json.dumps(payload),headers=headers,files=files)
r = requests.post('http://127.0.0.1:8080/api/create-company-profile/',data=payload,headers=headers,files=files)
print r.status_code
print r.text
Django code:-
class CompanyCreateApiView(CreateAPIView):
parser_classes = (MultiPartParser, FormParser,)
def post(self, request, *args, **kwargs):
print 'request ==', request.data
I tried your code with a
CreateAPIView
from the django-rest-framework. After fixing all the preliminary errors that your code produces, I could not reproduce a boundary error.Directory structure:
my_app/views.py:
myapp/serializers.py:
myapp/urls.py:
my_site/urls.py:
myapp/models.py:
my_site/settings.py:
Note that I didn't have to disable csrf tokens.
requests_client.py:
Output in request_client.py terminal window:
Output in django server window:
In order to use
requests
, I had to disablecsrf tokens
in my django project, otherwise I got errors when sending post requests:settings.py:
Next, your
payload
is not json data. json data is a string, but yourpayload
is a python dictionary. And, you can't just wrap quotes around your dictionary because json data cannot contain single quotes--which you used exclusively. So, you need to convert your python dictionary to json.Here's the
requests
client:See the Advanced Usage section of the
requests
docs.Here it is with a django class based view (albeit not a
Django Rest Framework
class view):Output in client window:
Output in django server window:
Apparently, the
requests <--> django mind meld
allows you to mix POST data with multipart/form-data, and you can do this:Coupled with this view:
I get the following output in the server window:
To allow for UTF-8 characters in your dictionary, you will have to take some extra steps when converting to json. See here.
When uploading file with parameters:
Don't overwrite the headers
Put other parameters together with upload_file in files dict.
Okay, I forgot about your headers. According to the spec:
Here is what a request containing multipart/form-data looks like:
See how the sections of data are separated by the boundary:
The idea is to use something for a boundary that is unlikely to appear in the data. Note that the boundary was included in the
Content-Type
header of the request.That request was produced by this code:
It looks like you were paying careful attention to the following note in the django-rest-framework docs:
But when you are using
requests
, if you specify theContent-Type
header yourself, thenrequests
assumes that you know what you're doing, and it doesn't overwrite yourContent-Type
header with theContent-Type
header it would have provided.You didn't provide the boundary in your
Content-Type
header--as required. How could you? You didn't assemble the body of the request and create a boundary to separate the various pieces of data, so you couldn't possibly know what the boundary is.When the
django-rest-framework
note says that you should include aContent-Type
header in your request, what that really means is:So @AChampion was exactly right in the comments: let
requests
provide theContent-Type header
, after all therequests
docs advertise:requests
works like this: if you provide afiles
keyword arg, then requests uses aContent-Type
header ofmultipart/form-data
and also specifies a boundary in the header; thenrequests
assembles the body of the request using the boundary. If you provide adata
keyword argument then requests uses aContent-Type
ofapplication/x-www-form-urlencoded
, which just assembles all the keys and values in the dictionary into this format:No boundary required.
And, if you provide both a
files
keyword arg and adata
keyword arg, then requests uses aContent-Type
ofmultipart/form-data
.By the way, here is a less painful way to read django's voluminous html error responses when using
requests
while developing:Then in your browser do:
and navigate to
error.html
. Read the error description in your browser, then track down what's wrong in your django project.Then come back to your terminal window and hit the up arrow key on your keyboard to get back to:
Hit
Return
, then refresh the browser window that is displayingerror.html
. Repeat as necessary. (Don't make the mistake of repeatedly refreshing your browser and thinking that is sending a new request--you have to run request_client.py again to send a new request).