Image upload fails with MultiValueDictKeyError in Django - python

I am calling the image upload API (Django REST API) from my view in a separate Django project.
My View
if request.method == 'POST' and request.FILES['file']:
try:
resp = requests.post(
"http://19.******/BankImage_API",
files = {"file" :request.FILES['file']},
headers={"content-type": "multipart/form-data",
"Authorization": "Token 71117971*************"
}, verify=False)
API
class Bankimageapi(APIView):
def post(self, request):
if request.method == 'POST' and request.FILES['file']:
try:
........
When I tried to upload an image, I got an error in API where FILES is <MultiValueDict: {}>:
django.utils.datastructures.MultiValueDictKeyError: 'file'
Please guide me to solve this problem.

In your view, the received request.FILES['file'] is an UploadedFile (see docs here), which is a subclass of File.
The requests library wants you to post the binary contents of the file. You can access the contents of the file using (file = request.FILES['file']):
read(): files={"file": file.read()}
file attribute: files = {"file": file.file} although I'm not sure this will give you control over whether or not this is binary.
Furthermore, you should remove the "content-type" header, which is added automatically by the requests package. This seems to mess up the body of your request.

What is the id of the input in the html?
Update:
The request.FILES is a dict that references the objects by their id on the html.

Related

POST request to upload file

I want to upload a file to my django application.
views.py
#api_view(['POST'])
def upload_to_evm(request):
if request.method == 'POST' and request.FILES['file']:
file = request.FILES['file']
filename = FileSystemStorage().save('abcd', file)
return JsonResponse({'Status': 'Successful'})
urls.py
urlpatterns = [
path('api/evm_process/', views.upload_to_evm)
]
Currently, I am sending my request with Binary File option and with header Content-Type: multipart/form-data and it gives MultiValueDictKeyError error which means my request.FILES is empty and I cannot understand why.
My question are:
What is the correct way to make a POST request with all the headers and query_params for the same ?
Do I need a parser (FileUploadParser, MultiPartParser or FormParser) to upload, save or process the uploaded file ?
Python version: 3.6.9
Django version: 3.2.3
headers
"content-type": "multipart/form-data;boundary=--------------- "
"X-CSRFToken": csrf_token
Here you have to set boundary. It is the string to separate the file name and type.
You need to import Multipartparser and FormParser.
Hope this help you.

Json parsing django rest framework

I want to parse incoming POST data in django views.py file
POST data:
{
"number" : "17386372",
"data" : ["banana","apple","grapes" ]
}
Here is how I tried to read above incoming data with request
views.py
class Fruits(APIView):
def post(self, request, format=None):
if request.method == "POST":
number = request.data.get('number')
fruits_data = json.loads(request.body)
if number not in [None, '', ' ']:
try:
response = {"return": "OK","data":fruits_data['data']}
return Response(response)
except:
return Response({"return": "NOT OK"})
else:
return Response({"return": "NOT OK"})
else:
return Response({"return": "NOT OK"})
ERROR:
You cannot access body after reading from request's data stream
The Django json parser does this already for you:
from rest_framework import parsers
class Fruits(APIView):
parser_classes = (parsers.JSONParser,)
def post(self, request, format=None):
number = request.data['number']
fruits = request.data['data']
If the Content-Type of your http request is already set properly to application/json you do not even need to specify the parser.
request.data and request.body are the two mechanisms, which reads the raw http request and construct data in a format, that is suitable to be used in python environment. Here the problem is that you are using both of them simultaneously. Thus, the inputstream of http connection is already read, by request.data call. Now request.body also tries to access the same stream, which doesn't contain now any data. Thus, it's throwing an error.
For you, I think following code will work :
fruits_data = json.loads(request.body)
number = fruits_data["number"]

Python - Post File to Django With Extra Data

I'm trying to have my django rest framework app accept file uploads. The uploads should be accompanied by additional data that is descriptive of the file and is necessary for post-processing. Uploading the file seems to work fine, however, I can't seem to get the django app to access the other data. For example, I have the file more_info.html which I am trying to upload to my app:
import requests
url = "http://www.example.com/fileupload"
files = {'file':open('more_info.html','rb')
data = {'brand':'my brand','type':'html','level':'dev'}
headers = {'Content-type': 'multipart/form-data', 'Content-Disposition': 'attachment; filename="more_info.html"'}
r = requests.post(url,files=files,data=,headers=headers)
In my Django view I am trying to view my POST data with the following:
def post(self, request):
print(request.POST)
print(request.FILEs)
Both print statements are returning:
{u'file': <InMemoryUploadedFile: more_info.html (multipart/form-data)>}
How can I access the rest of the data in the request POST?
This line
r = requests.post(url,files=files,data=,headers=headers)
You seem to be missing assigning data to data=.

Download file from url sent in request to django app

In my django app I am receiving a request with JSON like this:
{
"item1": false,
"item2": false,
"item3": "value",
"url": "http://downloadlink.example.net"
}
I have to save all data except url by which I just need to download text file and save it on server. How can I do the download part?
Code in view:
class Collect(View):
#csrf_exempt
def dispatch(self, request, *args, **kwargs):
return super(Collect, self).dispatch(request, *args, **kwargs)
def post(self, request):
data = json.loads(request.body.decode())
try:
item1 = data["item1"]
item2 = data["item2"]
item3 = data["item3"]
url = data["url"]
new_row = ModelName(
item1=item1,
item2=item2,
item3=item3,
)
new_row.save()
except Error:
return HttpResponse("Unable to save")
return HttpResponse("Saved")
So basically you can use urllib library and specifically its urlretrieve function to save the file in the temp folder locally first, then you can save it anywhere you want.
import urllib
response = urllib.urlretrieve(url)
contents = open(response[0]).read()
f = open('filename.ext','w')
f.write(contents)
f.close()
That is the basic example of how to save files from links. When you are saving files you should include the full path, so make sure to create a BASE_DIR constant in your settings file. You can go further and create a FileField or ImageField tables in your db, read here for more examples.
I think best practice solution is using celery because requesting another server while user is waiting is inappropriate and delays the user and sometimes it may take little long for bigger files. You can read the docs for more. I highly recommend it!

uploading files with urllib to a FileField model in Django

I have a django file model that has a models.FileField field and a form that is used to upload files to the server:
class UploadFile(model.Model):
filename = models.FileField(upload_to='uploads')
description = models.CharField(max_length=38, blank=True)
class UploadFileForm(ModelForm):
class Meta:
model = UploadFile
fields = ('filename', 'description')
This is how the view function looks like:
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
new_file = form.save()
Now I would like to have a python script that uses only the standard library to upload files to the UploadFile model using the view function above. However this code does not work because the POST request does not have a FILES method.
import urllib, urllib2
data = urllib.urlencode({'filename': open(uploadfile, "rb"),
'description': 'upload test'})
post_req = urllib2.Request(upload_file_url, data)
result = urllib2.urlopen(post_req)
How could I add a FILES method to the POST request to upload files using a python script? I have also tried to write a different view function that does not used the form but it is not working either.
To see whether your django view works, you could test it using a browser:
<FORM action="{{ upload_file_url }}"
enctype="multipart/form-data"
method="POST">
Description: <INPUT type="text" name="description" value="upload test"><BR>
File to upload: <INPUT type="file" name="filename"><BR>
<INPUT type="submit" value="Send">
</FORM>
It is complicated to replicate it programmatically using only stdlib.
To upload files as multipart/form-data you could use requests library:
import requests
response = requests.post(upload_file_url,
files={'filename': open(uploadfile,'rb'),
'description': 'upload test'})
print response.content
Or urllib2 and poster libraries:
import urllib2
import poster.encode
import poster.streaminghttp
opener = poster.streaminghttp.register_openers()
params = {'filename': open(uploadfile,'rb'), 'description': 'upload test'}
datagen, headers = poster.encode.multipart_encode(params)
response = opener.open(urllib2.Request(upload_file_url, datagen, headers))
print response.read()
It ain't that easy, is not that request doesn't have a FILES method (which is not a method, is a dictionary). The problem is that Django doesn't recognizes files if the request doesn't have the Content-Type:multipart/form-data.
Note that FILES will only contain data if the request method was POST and the that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
Here you can see how a request like that looks like: What should a Multipart HTTP request with multiple files look like?
https://docs.djangoproject.com/en/dev/ref/request-response/
So basically what you'll have to do is add that field to the request you're building and send it to django later.
You can take a look at this pages from django's doc to get more info about that and it will be great if you can sniff or see how the request is before sending it to django so you can know what is missing.
https://docs.djangoproject.com/en/dev/ref/forms/api/
Since there's not a easy/direct answer to this, I hope this can put you in the right direction.
Good luck!

Categories

Resources