I want to upload files from Backbone to Django File upload system.
First of all I've follow the https://stackoverflow.com/a/10916733/1590377 explanation. I've do a FileModel and with the above indication I have a model with this information:
attributes: Object
data: "data:image/png;base64,iVBORw ..."
file: "image2012-06-12 13:36:45.png"
now I save the model to the URL where I have the upload view in django like this:
def upload_file_64(request):
if request.method == 'POST':
file = cStringIO.StringIO(base64.b64decode(request.POST['data']))
#method to save the file
response_data={"result":"ok"}
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
else:
response_data={"success": "No a post request"}
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
but the response that the django sistem give me is:
"MultiValueDictKeyError at /api/upload64/↵'Key \'data\' not found in <QueryDict: {u\'base64,iVBORw0KG....
The POST http request is:
POST:
base64,iVBORw0KG ..."} = u''
{"file":"Captura de pantalla de 2012-06-12 13:36:45.png","data":"data:image/png = u''
How I can fix this so that I can upload a file to django. I use a multi-part method to upload files from another platforms how android but with backbone I can't upload a file.
Can someone help me eith this problem?
Thanks!!
I've coded another solution. I've used a jquery upload pluging to upload the file, and get the response.
The plugin is : http://lagoscript.org/jquery/upload/demo?locale=en and the code that I used in my backbone view is:
events : {
'change #file1' : 'upload'
},
upload : function(){
$('#file1').upload('http://192.168.0.195/api/upload/', function(res) {
console.log(res)
//now I use the res to create a model :)
}, 'html');
},
Related
I have a django 3.x project where I can upload multiple files and associated form data through the admin pages to a model called Document. However, I need to upload a large number of files, so I wrote a small python script to automate that process.
I am having one problem with the script. I can't seem to set the name of the file as it is set when uploaded through the admin page.
Here is the script...I had a few problems getting the csrf token working correctly, so there may be some redundant code for that.
import requests
# Set up the urls to login to the admin pages and access the correct add page
URL1='http://localhost:8000/admin/'
URL2='http://localhost:8000/admin/login/?next=/admin/'
URL3 = 'http://localhost:8090/admin/memorabilia/document/add/'
USER='admin'
PASSWORD='xxxxxxxxxxxxx'
client = requests.session()
# Retrieve the CSRF token first
client.get(URL1) # sets the cookie
csrftoken = client.cookies['csrftoken']
print("csrftoken1=%s" % csrftoken)
login_data = dict(username=USER, password=PASSWORD, csrfmiddlewaretoken=csrftoken)
r = client.post(URL2, data=login_data, headers={"Referer": "foo"})
r = client.get(URL3)
csrftoken = client.cookies['csrftoken']
print("csrftoken2=%s" % csrftoken)
cookies = dict(csrftoken= csrftoken)
headers = {'X-CSRFToken': csrftoken}
file_path = "/media/mark/ea00fd8e-4330-4d76-81d8-8fe7dde2cb95/2017/Memorable/20047/Still Images/Photos/20047_Phillips_Photo_052_002.jpg"
data = {
"csrfmiddlewaretoken": csrftoken,
"documentType_id": '1',
"rotation" : '0',
"TBD": '350',
"Title": "A test title",
"Period": "353",
"Source Folder": '258',
"Decade": "168",
"Location": "352",
"Photo Type": "354",
}
file_data = None
with open(file_path ,'rb') as fr:
file_data = fr.read()
# storage_file_name is the name of the FileField in the Document model.
#response_1 = requests.post(url=URL3, data=data, files={'storage_file_name': file_data,}, cookies=cookies)
response_2 = client.post(url=URL3, data=data, files={'storage_file_name': file_data, 'name': "20047_Phillips_Photo_052_002.jpg"}, cookies=cookies,)
When I upload using the admin page, the name of the file is "20047_Phillips_Photo_052_002.jpg", as it should be (i.e. storage_file_name.name = 20047_Phillips_Photo_052_002.jpg).
When I run the script using files={'storage_file_name': file_data,} (see response_1 at the bottom of the script), the files uploads correctly except that the name of the file is "storage_file_name" and not "20047_Phillips_Photo_052_002.jpg" (i.e. storage_file_name.name = "storage_file_name").
When I upload using files={'storage_file_name': file_data, 'name': "20047_Phillips_Photo_052_002.jpg"} the name of the file is still "storage_file_name" (i.e. storage_file_name.name = "storage_file_name").
I looked in the request.FILES object when uploading a file through the admin page, and the _name field for each object is the name of the file being uploaded. The documentation for the django File object says it has a field called name.
What am I missing to get my script to upload a file the same way as the admin page does? By that I mean, the name of the file is not "storage_file_name".
When I change the last response= line to
response = client.post(url=URL3, data=metadata, files= {'storage_file_name': open(file_path ,'rb'),}, cookies=cookies, headers=headers)
the file upload works and the file name is correctly displayed.
I'm having an issue in my django api file upload.
When I upload file using FileUploadParser everything goes well
but the file upload contains the header of the request stuff like Content-Disposition
when i try to open the uploaded file it is broken. I searched for a while for some solution but no chance. Decided to use MultiPartparser but this way nothing is included in the request.data dict. How can I go around this ? Can somebody show me a code or a way to successfully upload file or image to my api without having them broken ? Thanks for any hint.
Here's the code I have so far
class EstablishmentMediaUploadView(views.APIView):
permission_classes = (IsAuthenticated,)
authentication_class = JSONWebTokenAuthentication
parser_classes = (FileUploadParser,)
serializer_class = MediaSerializer
name = 'establishment-media-file-upload'
def put(self, request, **kwargs):
print(request.data)
if 'file' not in request.data:
raise ParseError("Empty media file for establishment")
establishmentid = kwargs.get('establishmentid')
if establishmentid is None:
return Response({"error": "You didn't specify the establishmentid"}, status=400)
mediaFile = request.data.get('file')
media = Media.objects.create(mediatitle=mediaFile.name)
establishment = Establishment.objects.get(id=establishmentid)
media.establishmentlogo.save(mediaFile.name, mediaFile, save=False)
media.establishment = establishment
media.save()
return Response({"message": "Logo added for this establishment"}, status=200)
I first test it by uploading from Insomnia api test client and Vscode thunder client extension. For both the headers are included.
Then I did the test in my angular frontend. Here's the code of the service method in charge of the upload :
setEstablishmentLogo(establishment: Establishment, media: Media): Observable<Object> {
let formdata = new FormData();
formdata.set("establishmentlogo", media.establishmentlogo);
var url = `${endpoints.establishment_media_upload_uri_base}/${establishment.id}`;
console.log(url)
return this.http.put(url, formdata, {
headers: {
"Accept": "*/*",
"Content-Disposition": `attachment; filename=${media.establishmentlogo.name}`,
'Authorization': `Bearer ${token}`
}
});
}
Got the same behavior. Perhaps I'm getting something wrong in all this. But can't figure it out.
Well i use flutter framework and i upload images from mobile to django and guess what, it updates image field without doing anything from my end. Just make sure where ever you are uploading this image from do it accordingly to http library in that framework.
So just to avoid this headache to another folk. Here is what I found finally.
I was using FileUploadParser in my uploadview. But the point is that when using the 'FileUploadParser' the request.data dict
is populated with the uploaded content. The uploaded content in that case, when it comes from clients like Postman
or Insomnia or Thunder Client of vscode, it contains the single file you uploaded (We assume that you select the option binary request
in those clients) and that way, nothing wrong happens, the files are safe. But when you upload files from the browser, let's say from angular and the parser_class in the view is set to FileUploadParser
then you will successfully upload the file but it would be broken because the whole request (file + headers and browser boundaries stuffs) is parsed as a single file
by FileUploadParser(It's indeed it's job) and thus you end up with broken files on your server or backend. So the way to go around that is to set parser_classes to MultipartParser and FormParser optionally
That way your uploaded files are fine. And much, you don't need to specify Content-Disposition: attachment; filename='some file name' in your request header. Bear in mind that when you use FileUploadParser, your file is in request.data['file'] and when it is MultiPartParser (and FormParser) the file is in request.FILES['file'].
With all this said, the working version of the upload view I posted in my question looks like below :
class EstablishmentMediaUploadView(views.APIView):
permission_classes = (IsAuthenticated,)
authentication_class = JSONWebTokenAuthentication
parser_classes = (MultiPartParser, FormParser,)
name = 'mtp-establishment-media-file-upload'
def post(self, request, *args, **kwargs):
if 'file' not in request.FILES:
return Response({"message": "Please provide a file"}, status=status.HTTP_400_BAD_REQUEST)
establishmentid = kwargs.get('establishmentid')
if establishmentid is None:
return Response({"error": "You didn't specify the establishmentid"}, status=status.HTTP_400_BAD_REQUEST)
mediaFile = request.FILES['file']
media = Media.objects.create(mediatitle=mediaFile.name)
media.establishmentlogo.save(mediaFile.name, mediaFile, save=True)
try:
establishment = Establishment.objects.get(id=establishmentid)
except Establishment.DoesNotExist:
return Response({"error": "Establishment does not exist"}, status=status.HTTP_404_NOT_FOUND)
media.establishment = establishment
media.save()
return Response({"message": "Logo added for this establishment"}, status=status.HTTP_201_CREATED)
I'm trying to use an external API to grab data for my project to show on the template.
service.py
def get_data(title, url, description, body, datePublished):
url = 'https://contextualwebsearch-websearch-v1.p.rapidapi.com/api/Search/WebSearchAPI'
params = {"autoCorrect": "true", "pageNumber": "1", "pageSize": "10", "q": "police", "safeSearch": "true" }
r = requests.get(url, params=params)
data = r.json()
article_data = {'data': data['value']}
return article_data
Then I show it on views.py
...
import service
class IndexData(TemplateView):
def get(self, request):
article_data = service.get_data.all()
return render(request, 'pages/home.html', article_data)
but I'm getting ModuleNotFoundError: No module named 'service'
Did i miss something?
Answered my own problem with the help from Saint Peter on freeCodeCamp's discord channel.
Apparently, in cookiecutter-django, you have to pass from the project to the app to the view before you can import something like this:
from project_name.app_name import services
Cheers
Is the main folder a python package? Does it contains the "init.py" file?
If not, try to add it and retry - if it give you errors please share the file hirecracy view (even scrubbed) to have more informations
PS: if you add the init file, make sure to edit all other files that calls on that name reference e.g. "main_folder.sub_file" become just "sub_file"
I want to submit a form to upload either a csv file or an excel file, and provide additional information provided in the form, e.g. text.
For defining the form and sending the request I use React Bootstrap with axios, and for the api I'm using Django 2.2 with rest-framework.
request.FILES is always an empty dict, even if I try setting the Content-Type header to multipart/form-data, and then additionally nothing is found in the post data of the request, and printing request.data.get('information') below shows None in the Django log. Please help, any support will be greatly appreciated.
See below for code snippets.
Form definition in render():
<form onSubmit={this.handleSubmit}>
<FormGroup>
<Form.Label>Upload file</Form.Label>
<Form.Control type="file" onChange={this.handleFile}></Form.Control>
</FormGroup>
<Button block bssize="large" disabled={!this.validateForm()} type="submit">
Submit
</Button>
</form>
Handling file change:
handleFile = event => {
event.preventDefault();
const fileToUpload = event.target.files[0]
this.setState({
fileToUpload: fileToUpload
})
}
Sending the request:
const payload = {
file: this.state.fileToUpload,
information: "somedata"
}
const contentType = "multipart/form-data"
let accessToken = token
var headers = {"Authorization" : `Bearer ${accessToken}`}
headers['Content-Type'] = contentType
const response = await axios({
method: "post",
data: payload,
url: url,
headers: headers
});
Django endpoint definition:
class RegisterData(views.APIView):
parser_classes = (parsers.JSONParser, parsers.MultiPartParser)
def post(self, request):
for file in request.FILES.values():
print(file)
info = request.data.get('information')
print(info)
return Response({"success": "Good job, buddy"})
The answer is in the comments but I'll put it here so it's more visible to users with these issues:
Check that you are using the correct data types and arguments for axios:
How to post a file from a form with Axios
I'm trying to test my Django REST API for file uploading.
The catch is that my server tries to validate the file so that the file has a recognised file type. Currently only text-based filetypes are allowed, like: docs, pdf, txt.
I've tried using a temporary file and now I just tried to read a file from the disk then gave up.
Whenever I use a temporary file the server responds with:
{
"cover_letter": [
"The submitted data was not a file. Check the encoding type on the form."
],
"manuscript": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
This is my test:
def test_user_can_submit_a_paper(self):
"""
Ensure that an user is able to upload a paper.
This test is not working.. yet
"""
tmp_file = open("__init__.py", "w")
data = {
"title": "paper",
"authors": "me",
"description": "ma detailed description",
"manuscript": base64.b64encode(tmp_file.read()).decode(),
"cover_letter": base64.b64encode(tmp_file.read()).decode()
}
tmp_file.close()
response = self.client.post(self.papers_submitted, data=urlencode(MultiValueDict(data)), content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION=self.authorization_header)
print(response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
del data["cover_letter"]
response = self.client.post(self.papers_submitted, data=urlencode(MultiValueDict(data)), content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION=self.authorization_header)
print(response.data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
I have successfully tested this endpoint via postman but I don't know how to do it in Python.
If I understand what you are asking, you'd like to test your DRF endpoint with a file upload, using python. You should use RequestFactory, which simulates a request -- it will be more like using Postman.
This answer Django: simulate HTTP requests in shell has an example of using RequestFactory, and there are multiple answers here django RequestFactory file upload for specific solutions on how to include uploaded files with RequestFactory.
I've managed to come up with a solution thanks to Mark's answer.
Here's the code if anyone is interested:
def test_user_can_submit_a_paper(self):
from api.journal import PaperListSubmitted
from django.test.client import RequestFactory
from django.core.files import temp as tempfile
"""
Ensure that an user is able to upload a paper.
"""
request_factory = RequestFactory()
manuscript = tempfile.NamedTemporaryFile(suffix=".txt")
cover_letter = tempfile.NamedTemporaryFile(suffix=".txt")
manuscript.write(b"This is my stupid paper that required me to research writing this test for over 5h")
cover_letter.write(b"This is my stupid paper that required me to research writing this test for over 5h")
manuscript.seek(0)
cover_letter.seek(0)
post_data = {
"title": "My post title",
"description": "this is my paper description",
"authors": "no authors",
"manuscript": manuscript,
"cover_letter": cover_letter
}
request = request_factory.post(self.papers_submitted, HTTP_AUTHORIZATION=self.authorization_header,
data=post_data)
response = PaperListSubmitted.as_view()(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)