django how to upload folder - python

I know how to upload multiple files through django, but I have a problem when uploading a folder if there are subfolders in it. The django can't receive subfolders. I found the reason, because browser use '.' to represent a folder, but django can't parse it then stop parsing. Is there an elegant way to fix it?
python code:
def uploader_single(request):
data = {}
if request.method == 'POST':
if True:
for afile in request.FILES.getlist('file'):
new_file = UploadFileSingle(file = afile)
new_file.save()
return HttpResponseRedirect('')
else:
print "form is not valid"
return HttpResponseRedirect('')
else:
print 'not post'
Python code:
class UploadFileSingle(models.Model):
file = models.FileField(upload_to='files/%Y/%m/%d', models.FilePath)
uploaded_at = models.DateTimeField(auto_now_add=True)
models.FilePathField.recursive = True
models.FilePathField.allow_folders = True
updated_at = models.DateTimeField(auto_now=True)
def some_folder = FilePathField(path='some_path', recursive=True, allow_files=True, allow_folders=True,)'
HTML code:
<input type="file" name="file" multiple = "true" webkitdirectory="true" directory = "true"/>

There is newer topic which asks the same question and I have tried to answer:
Django directory upload get sub-directory names
Basically it is the default behavior of Django if you want to have different behavior you need to write your own upload handlers

I came up with easy solution for this problem.
You could get folder name via html and javascript in frontend
pass it as a value to hidden form field
in backend you can create a directory with that name
and upload files in this directory.
HTML
<input type="hidden" name="dir_name" id="id_dir_name">
<input type="file" name="file" onchange="selectFolder(event)" webkitdirectory="" multiple="" required="" directory="" id="id_file">
JS
function selectFolder(e) {
var theFiles = e.target.files;
var relativePath = theFiles[0].webkitRelativePath;
var folder = relativePath.split("/");
$("#" + id).val(folder[0]);
}
views
directory_name = form.cleaned_data['dir_name']
os.mkdir(os.path.join(settings.MEDIA_ROOT, directory_name))
handle_uploaded_file(request.FILES['file'], directory_name)

you can use django filer
rerfer:https://github.com/stefanfoulis/django-filer

Related

Cannot locate csv file when the trigger comes from the frontend with django

In a few words, I want the user to give a text input at a frontend form in order to define the dataset that wants to examine, for instance 'data.csv'.
This 'data.csv' is passed as a string variable to a python function that needs to open the dataframe with the specific name. The error is:
FileNotFoundError at /
[Errno 2] File b'data.csv' does not exist: b'data.csv'
Request Method: POST
Request URL: http://127.0.0.1:8000/
Django Version: 2.2.5
Exception Type: FileNotFoundError
Exception Value:
[Errno 2] File b'data.csv' does not exist: b'data.csv'
The file data.csv is in the same directory with the file beta.py that calls the file in order to read it.
Moreover, if I don't pass any value from the frontend side, but I just trigger the script, and instead of dataframe = pd.read_csv(file), I have dataframe = pd.read_csv('data.csv'), it gives me the same error, but if i execute the script from the command line, then everything runs just fine. So I can't figure why the script cannot locate data.csv if the trigger comes from the frontend.
These are the codes.
PS: Thank you all in advance so much for the help
views.py
def get_name(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
file = form.cleaned_data.get('your_name')
beta.hello(file)
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'home.html', {'form': form})
beta.py
import pandas as pd
def hello(file):
dataframe = pd.read_csv(file)
home.html
<form action="/" method="post">
{% csrf_token %}
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name">
<input type="submit" value="OK">
</form>
UPDATE:
def hello(s):
file = 'data.csv'.encode("utf-8")
print(type(file))
#dataframe = pd.read_csv(io.StringIO(str(s)))
dataframe = pd.read_csv(io.BytesIO(file))
print("---")
print(dataframe)
*** Results ***
<class 'str'>
<class 'bytes'>
---
Empty DataFrame
Columns: [data.csv]
Index: []
It's almost always a good idea to build absolute paths in Django, as you're never sure of the interpreter's location at the time of execution. In your case, django is most likely looking for data.csv in your project's base directory and not where your views.py is located.
import pandas as pd
from django.conf import settings
def hello(file):
print(settings.BASE_DIR)
fpath = os.path.join(
settings.BASE_DIR,
'appname',
file
)
dataframe = pd.read_csv(fpath)
If that doesn't work, BASE_DIR is probably pointing to projectname/projectname/ (check the printed output from hello()). Create a new variable in settings.py and use that instead of BASE_DIR (this happens in my case for some reason):
# Build paths inside the project like this: os.path.join(settings.TOP_DIR,...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TOP_DIR = os.path.dirname(BASE_DIR)

request.FILES.getlist('file') is empty

I am sending few files to the request with dropzone.js but the request.FILES.getlist() seems to be completely empty. Any possible reasons as to why?
--sorry that was just a typo in my question. it is FILES in my code.
def upload(request):
user = request.user
theAccount = user.us_er.account
if request.method == "POST":
form = uploadForm(request.POST)
if form.is_valid():
descriptions = request.POST.getlist('descriptions')
count = 0
for f in request.FILES.getlist('file'):
theAccount.file_set.create(docFile = f, description = descriptions[count], dateAdded = timezone.now(), creator = user.username)
count = count + 1
return HttpResponseRedirect('/home/')
else:
return HttpResponse("form is not valid")
else:
return HttpResponse('wasnt a post')
this is my template containing with the dropzone.
<form method="POST" style="border: 2px solid green;" action= "/upload/" enctype="multipart/form-data" class="dropzone">
{% csrf_token %}
<div class="dropzone-previews"></div>
<button value=" submit" class="btn btn-success" type="submit" id="submit">Press to upload!</button>
</form>
I know this is an old question, but for the sake of people landing here through google.
Dropzone.js uses ajax to upload files in a queue, so your endpoint should process the upload as a singular file not multiple.
You can access the file via request.FILES['file']
Instead of doing this:
descriptions = request.POST.getlist ('descriptions')
Try it like this:
descriptions = request.FILES.getlist ('descriptions []')
In my case it worked.
The reason for that is Dropzone's way of handling the FormData name.
TLDR
To make Dropzone Django-compliant set Dropzone's paramName option to:
paramName: (index) => 'file',
Explanation
The Django way of doing multiple files upload would be adding all the files into the FormData with the same name "file" (or whatever name you gave to Dropzone paramName option) that would end up in a single MultiValueDict called "file" in request.FILES
If we look at Dropzone's documentation, it states the following for paramName:
The name of the file param that gets transferred. NOTE: If you
have the option uploadMultiple set to true, then Dropzone will append
[] to the name.
That documentation is outdated or misleading because Dropzone does more than that according to its source code:
// #options.paramName can be a function taking one parameter rather than a string.
// A parameter name for a file is obtained simply by calling this with an index number.
_getParamName(n) {
if (typeof this.options.paramName === "function") {
return this.options.paramName(n);
} else {
return `${this.options.paramName}${
this.options.uploadMultiple ? `[${n}]` : ""
}`;
}
}
if uploadMultiple option is set to true, Dropzone will add an index between the square brackets and add them to the FormData with a name like "file[n]" where n is the index of the file.
The result in Django is a request.FILES with 'n' MultiValueDict called file[n] and you would need to call request.FILES.getlist('file[n]') for n = 0 to the number of files you sent.
Fortunately, as you can see in the comment for the function above (and that is missing from the doc), paramName can also be a function.
You can override this logic by simply ignoring the index and returning the paramName you want to handle in Django like this:
JS, Dropzone option
paramName: (n) => 'my_param_name',
Python, views.py
files = request.FILES.getlist('my_param_name')

Create folders in flask

I want to create folders using flask . I Google for this a lot but didn't find any help. Evey search shows me the folder structure of flask but I want to know how can I create folders using code.
Actually I want to create folders for every user at register time.So suggest me a way how can I create a simple empty folder at particular path in flask.
Use os.mkdir or os.makedirs according to your need.
import os
os.makedirs('/path/to/directory')
create folders for every user at register time.
You can override the default constructor of sqlalchemy orm like this and do custom stuff
inside the function. Here is semi working code.
from app import db
import os
class User(db.Model):
# ...
folder = db.Column(db.String(80), unique=True, nullable=False)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
# do custom stuff call folder create method here
self.create_folder()
# method to create folder
def create_folder(self):
#give path to location where you want to create folder
path = "my path where i want to create folder"
os.mkdir(path)
#update some property in User database
self.folder = "path to my user folder"
from flask import Flask, request
import os
app = Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['UPLOAD_FOLDER'] = 'static/users/'
#app.route('/register', methods=["POST", "GET"])
def register():
username = request.form.get("username")
if request.method == "POST":
username = username.strip().capitalize()
user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username)
os.mkdir(user_folder)
return f"folder is created under the name {username} and the full path is {user_folder}"
return """
<form method="post" action="/register">
<input type="text" name="username" required>
<button type="submit" >Submit</button>
</form>
"""

Google App Engine (Python) - Uploading a file (image)

I want the user to be able to upload images to Google App Engine. I have the following (Python):
class ImageData(ndb.Model):
name = ndb.StringProperty(indexed=False)
image = ndb.BlobProperty()
Information is submitted by the user using a form (HTML):
<form name = "input" action = "/register" method = "post">
name: <input type = "text" name = "name">
image: <input type = "file" name = "image">
</form>
Which is then processed by:
class AddProduct(webapp2.RequestHandler):
def post(self):
imagedata = ImageData(parent=image_key(image_name))
imagedata.name = self.request.get('name')
imagedata.image = self.request.get('image')
imagedata.put()
However, when I try to upload an image, lets say "Book.png", I get the error:
BadValueError: Expected str, got u'Book.png'
Any idea what is going on? I have been working with GAE for quite some time, but this is the first time I had to use blobs.
I used this link: https://developers.google.com/appengine/docs/python/images/usingimages
which uses db, not ndb.
I also tried storing the image in a variable first like in the link:
storedInfo = self.request.get('image')
and then storing it:
imagedata.image = ndb.Blob(storedInfo)
Which ALSO gives me an error:
AttributeError: 'module' object has no attribute 'Blob'
Thanks in advance.
Had the same prob.
just replace
imagedata.image = self.request.get('image')
with:
imagedata.image = str(self.request.get('image'))
also your form needs to have enctype="multipart/form-data
<form name = "input" action = "/register" method = "post" enctype="multipart/form-data">
There is a great example in the documentation that describes how to upload files to the Blobstore using a HTML form: https://developers.google.com/appengine/docs/python/blobstore/#Python_Uploading_a_blob
The form should point to a url generated by blobstore.create_upload_url('/foo') and there should be a subclass of the BlobstoreUploadHandler at /foo like this:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file')
blob_info = upload_files[0]
imagedata = ImageData(parent=image_key(image_name))
imagedata.name = self.request.get('name')
imagedata.image = blob_info.key()
imagedata.put()
For this to work, you should change your data model such that in ImageData, image referes to a ndb.BlobKeyProperty().
You can serve your image simply from a url generated by images.get_serving_url(imagedata.image), optionally resized and cropped.
You must add enctype="multipart/form-data" to your form in order for this to work
<form name = "input" action = "/register" method = "post" enctype="multipart/form-data">
name: <input type = "text" name = "name">
image: <input type = "file" name = "image">
</form>

Handling file submissions with html and python

I'm trying to create a form that will handle video file uploads from the user, but I'm running into a bunch of problems. I am trying to avoid building a model form for this because I won't be saving the file to my database long term. I just want to get it to the server so that I can submit it to youtube. The html I have is:
<form method='post' action='/new/' enctype="multi-part/form-data">{% csrf_token %}
<input type='file' name='file' id='file'/>
<input type='submit' />
</form>
and then the view attempts to handle the file like so:
def create_video(request):
if request.method == 'POST':
video = request.POST['file']
command=subprocess.Popen('youtube-upload --email=' + email + ' --password=' + password + '--title=' + title + ' --description=' + description + ' --category=Sports ' + video, stdout=subprocess.PIPE)
vid = command.stdout.read()
# do stuff to save video instance to database
return show_video(request, video.id)
else:
form=Video()
return render_to_response('create_video.html', RequestContext(request, locals()))
note: youtube-upload is a python module to upload videos to youtube with that given command.
So for starters when I submit the form from the front end django sends a message saying "Key 'file' not found in <QueryDict:...
and given that I fix the form so that it will submit properly is the rest of the view properly handling the file?
request.POST doesn't contain file upload information. You need to use request.FILES.

Categories