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!
Related
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.
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.
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=.
I have a simple view which receives files form users. I've tested it with my browser and it works okay.
On the other hand, I have a python and tested it against requestb.in and I can see the multipart/form-data and a http response code 200. So, both my view in receiving and storing files and the script in uploading multipart data work fine.
My problem being: When I use my script to upload a file to the view, even though I get http status code 200, I see no files being stored on the disk while in case of using the browser I do. What would be the problem you think? Here are my model, view and script:
models.py:
class FileUploads(models.Model):
uploads = models.FileField()
forms.py:
from django import forms
from .models import FileUploads
class UserProfileForm(forms.ModelForm):
class Meta:
model = FileUploads
fields = ['uploads']
views.py:
#csrf_exempt
def upper(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
context = {'title': 'welcome', 'form': form}
if form.is_valid():
form.save()
context = {'form': form, 'title': 'thanks'}'
return render(request, 'upform.html', context)
Python script up.py:
import requests
f = open('C:\\Users\\Amir\\Desktop\\snow.jpg', 'rb')
urls='http://127.0.0.1:8000/upper'
r=requests.post(urls, files= {'a':f})
print(r.status_code)
It should be:
import requests
f = open('C:\\Users\\Amir\\Desktop\\snow.jpg', 'rb')
urls='http://127.0.0.1:8000/upper'
r=requests.post(urls, files= {'uploads':f})
print(r.status_code)
...the name of the file in the dict passed to requests.post needs to match the field name in the Django form :)
I am trying to let user provide a url and django will download automatically but have no idea what to do. Thanks in advance.
My advice is you use celery and request to asynchroniusly download the files. There's a site that explains thoroughly how to integrate Django and Celery
You can retrieve a url using the requests library.
To get the URL from the user, you can use a form.
Then, in the view, you can do something like
if request.method == 'POST': # If the form has been submitted...
form = URLForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
response = requests.get(form.entered_url,stream=True)
with open('save_file','w') as f:
for chunk in response.iter_content():
f.write(chunk)
return HttpResponseRedirect('/thanks/') # Redirect after POST