Django dynamic file upload path - python

Here its my django code.
I want to upload my file on specific location and that path is created dynamically.
def get_upload_file(instance, filename):
today_date = datetime.datetime.today().date()
directory = 'Data/'+ str(today_date)
if not os.path.exists(directory):
os.makedirs(directory)
full_path = str(directory)+"/%s" %(filename)
print "full_path --> ",full_path
# user = updated_path()
# print user
return full_path
class UploadFile(models.Model):
path = models.FileField(upload_to=get_upload_file)
I am trying above code to upload file but i want to another directory in it and its name is on username.
expected output
Data/2015-08-16/username/
then i want to upload file in username directory
any solution please help me

Finally I got a solution for the above problem. When I am creating class instance set request object to that instance and access that request object in get_upload_file function
class UploadFile(models.Model):
path = models.FileField(upload_to=get_upload_file)
reqObj = None
def set_reqObj(self, request):
self.reqObj = request
new_file = UploadFile(path = afile)
new_file.set_reqObj(request)
use reqObj in get_upload_file function
instance.reqObj.user.username
updated get_upload_file function is
def get_upload_file(instance, filename):
today_date = datetime.datetime.today().date()
directory = 'Data/'+ str(today_date)+'/'+instance.reqObj.user.username
if not os.path.exists(directory):
os.makedirs(directory)
full_path = str(directory)+"/%s" %(filename)
return full_path

Related

passing a filename as a parameter from a for loop in python flask

I am uploading file so i am trying to pass a filename after looping through but i am getting an unbountLocalerror. i have tried making the filename global so that it can be seen outside the for loop but its throwing filename not defined. what other way can i use to get the filename so i can use it in html/jinja
#app.route('/upload', methods=['POST'])
def upload():
target = os.path.join(APP_ROOT,'static/')
if not os.path.isdir(target):
os.mkdir(target)
else:
for upload in request.files.getlist('file'):
filename = upload.filename
destination = "/".join([target, filename])
upload.save(destination)
return render_template("upload.html",filename=filename)
The error occurred because you created the local variable inside the else statement.
If, for example the condition was that it triggered the if: part
of the code, the local variable filename would never be created.
Therefore, the unbound error occurred when you tried to access it in return render_template("upload.html",filename=filename).
It also seems that you wanted to return multiple renders - because you don't have just one filename but bunch of them.
I changed the function to return the list of render_template objects created based on filenames list that is being appended on line 10 ( filenames.append(upload.filename)).
Code:
#app.route('/upload', methods=['POST'])
def upload():
target = os.path.join(APP_ROOT,'static/')
if not os.path.isdir(target): #Create the target folder if it doesnt exitst
os.mkdir(target)
filenames = []
for upload in request.files.getlist('file'): #Upload files into the folder
filenames.append(upload.filename)
destination = "/".join([target, upload.filename])
upload.save(destination)
return [render_template("upload.html",filename = filename) for filename in filenames] #Return therender_template

QT: how to save a generated file by the program into user's machine

I have a PyQt program which takes a file from user to be processed which then create a new file accordingly. i want the user to be able to download/save the new created file into their machine/desktop...
this is the uploading code:
def open_dialog_box(self):
#To just print the name of file
filename = QFileDialog.getOpenFileName()
filename = filename[0]
basename = os.path.basename(filename)
def pushButton_2_handler(self):
path = QFileDialog.getOpenFileName()
path = path[0]
basename = os.path.basename(path)
filename, file_extension = os.path.splitext(basename)
print(path)
print(filename)
print(file_extension)
self.textBrowser_fileSelectedName.clear()
self.textBrowser_fileSelectedName.append(str(basename))
You want to use another QFileDialog but this one will be setup to return the selected folder/custom name the user typed.
def save_file(self):
# Open a dialog so that the user can select a target location.
dialog = QFileDialog()
dialog.setWindowTitle("Select a location to save your file.")
dialog.setAcceptMode(QFileDialog.AcceptSave)
dialog.exec_()
if not dialog.selectedFiles(): # If the user closed the choice window without selecting anything.
return
else:
path_to_file = dialog.selectedFiles()[0]
# Create that file and work on it, for example:
with open(path_to_file + ".txt") as file:
file.write("Hello there!")

Cant change string value to variable with string value

I upload file to dropbox api, but it post on dropbox all directories from my computer since root folder. I mean you have folder of your project inside folder home, than user until you go to file sours folder. If I cut that structure library can't see that it is file, not string and give mistake message.
My code is:
def upload_file(project_id, filename, dropbox_token):
dbx = dropbox.Dropbox(dropbox_token)
file_path = os.path.abspath(filename)
with open(filename, "rb") as f:
dbx.files_upload(f.read(), file_path, mute=True)
link = dbx.files_get_temporary_link(path=file_path).link
return link
It works, but I need something like:
file_path = os.path.abspath(filename)
chunks = file_path.split("/")
name, dir = chunks[-1], chunks[-2]
which gives me mistake like:
dropbox.exceptions.ApiError: ApiError('433249b1617c031b29c3a7f4f3bf3847', GetTemporaryLinkError('path', LookupError('not_found', None)))
How could I make only parent folder and filename in the path?
For example if I have
/home/user/project/file.txt
I need
/project/file.txt
you have /home/user/project/file.txt and you need /project/file.txt
I would split according to os default separator (so it would work with windows paths as well), then reformat only the 2 last parts with the proper format (sep+path) and join that.
import os
#os.sep = "/" # if you want to test that on Windows
s = "/home/user/project/file.txt"
path_end = "".join(["{}{}".format(os.sep,x) for x in s.split(os.sep)[-2:]])
result:
/project/file.txt
I assume the following code should works:
def upload_file(project_id, filename, dropbox_token):
dbx = dropbox.Dropbox(dropbox_token)
abs_path = os.path.abspath(filename)
directory, file = os.path.split(abs_path)
_, directory = os.path.split(directory)
dropbox_path = os.path.join(directory, file)
with open(abs_path, "rb") as f:
dbx.files_upload(f.read(), dropbox_path, mute=True)
link = dbx.files_get_temporary_link(path=dropbox_path).link
return link

Handling uploaded files in HTML form in django WITHOUT django forms

The problem is that I have an HTML form that is generated dynamically, therefore I don't know how many file/text inputs are going to be there. So I decided not to use a form and handle file upload through request.FILES.
This is my code so far.
elif request.method == "POST":
for ta in text_attachments:
ta.text = request.POST["text-" + ta.id.__str__()]
ta.save()
for fa in text_attachments:
if request.FILES.get("file-" + fa.id.__str__(), None):
# fa.file = request.FILES["file-" + fa.id.__str__()]
handle_uploaded_file(fa, request.FILES["file-" + fa.id.__str__()])
fa.save()
And this is the handle_uploaded_file function:
def handle_uploaded_file(instance, f):
path = coopertaion_attachment_path(instance, f.name)
with open(path, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
And this is the function that decides where the file should be uploaded.
def coopertaion_attachment_path(instance, filename):
print("Filename: " + filename)
os.umask(0)
path = 'applications/cooperation/%s/' % instance.application.user.id
att_path = os.path.join(settings.MEDIA_ROOT, path)
if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage":
if not os.path.exists(att_path):
os.makedirs(att_path, 0o777)
print("Creating path...")
return os.path.join(path, filename)
In the end I always get this error:
[Errno 2] No such file or directory: 'applications/cooperation/1/filename.pdf'
Is it even possible to handle uploaded files without django form? How do I assign the uploaded file to a FileField of one of my models?
You seem to be doing it right (file uploading and handling). Problem is in your cooperation_attachment_path method. You are returning os.path.join(path, filename) where path holds relative to your project path which does not exist hence the error.
[Errno 2] No such file or directory:
'applications/cooperation/1/filename.pdf'
att_path holds absolute path so you should do:
return os.path.join(att_path, filename)

How to change the file name of an uploaded file in Django?

Is it possible to change the file name of an uploaded file in django? I searched, but couldn't find any answer.
My requirement is whenever a file is uploaded its file name should be changed in the following format.
format = userid + transaction_uuid + file_extension
Thank you very much...
How are you uploading the file?
I assume with the FileField.
The documentation for FileField.upload_to says that the upload_to field,
may also be a callable, such as a
function, which will be called to
obtain the upload path, including the
filename. This callable must be able
to accept two arguments, and return a
Unix-style path (with forward slashes)
to be passed along to the storage
system. The two arguments that will be
passed are:
"instance": An instance of
the model where the FileField is
defined. More specifically, this is
the particular instance where the
current file is being attached.
"filename":The filename that was
originally given to the file. This may
or may not be taken into account when
determining the final destination
path.
So it looks like you just need to make a function to do your name handling and return the path.
def update_filename(instance, filename):
path = "upload/path/"
format = instance.userid + instance.transaction_uuid + instance.file_extension
return os.path.join(path, format)
You need to have a FileField with the upload_to that calls to a callback, see [1]
Your callback should call a wrapper method which gets an instance as one of the params and filename as the other. [2]
Change it the way you like and return the new path [3]
1. LOGIC
FileField(..., upload_to=method_call(params),....)
2. define method
def method_call(params):
return u'abc'
3. Wrapper:
def wrapper(instance, filename):
return method
this is the rapper method that you need for getting the instance.
def wrapper(instance, filename):
... Your logic
...
return wrapper
Complete Code
def path_and_rename(path, prefix):
def wrapper(instance, filename):
ext = filename.split('.')[-1]
project = "pid_%s" % (instance.project.id,)
# get filename
if instance.pk:
complaint_id = "cid_%s" % (instance.pk,)
filename = '{}.{}.{}.{}'.format(prefix, project, complaint_id, ext)
else:
# set filename as random string
random_id = "rid_%s" % (uuid4().hex,)
filename = '{}.{}.{}.{}'.format(prefix, project, random_id, ext)
# return the whole path to the file
return os.path.join(path, filename)
return wrapper
Call to Method
sales_attach = models.FileField("Attachment", upload_to=path_and_rename("complaint_files", 'sales'), max_length=500,
help_text="Browse a file")
Hope this helps.
Thanks.
if you want your function re-usable:
import hashlib
import datetime
import os
from functools import partial
def _update_filename(instance, filename, path):
path = path
filename = "..."
return os.path.join(path, filename)
def upload_to(path):
return partial(_update_filename, path=path)
You just have to use it this way:
document = models.FileField(upload_to=upload_to("my/path"))
import random
import os
def generate_unique_name(path):
def wrapper(instance, filename):
extension = "." + filename.split('.')[-1]
filename = str(random.randint(10,99)) + str(random.randint(10,99)) + str(random.randint(10,99)) + str(random.randint(10,99)) + extension
return os.path.join(path, filename)
return wrapper
#You just have to use it this way:#
photo = models.FileField("Attachment", upload_to=generate_unique_name("pics"),max_length=500,help_text="Browse a photo")
Incase this may help anyone.
import os
import uuid
import random
from datetime import datetime
def user_directory_path(instance, filename):
# Get Current Date
todays_date = datetime.now()
path = "uploads/{}/{}/{}/".format(todays_date.year, todays_date.month, todays_date.day)
extension = "." + filename.split('.')[-1]
stringId = str(uuid.uuid4())
randInt = str(random.randint(10, 99))
# Filename reformat
filename_reformat = stringId + randInt + extension
return os.path.join(path, filename_reformat)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
That much code is not needed you can just use single line code
fille._name=userid + transaction_uuid + file_extension
Like
class xyz(models.Model):
file = models.FileField(upload_to="notice/")
def add(request):
file = request.POST['file']
file._name = request.user.id + transaction_uuid +"."+ file._name.split('.')[1]
you can overwrite file name by overwriting _name value of file object.
The basic way is
import os
os.rename('a.txt', 'b.html')
For your situation, it would probably look like
os.rename ("a.txt", "{id}{uuid}.{ext}".format(id=userid, uuid=transaction_uuid, ext=file_extension))
Hi, i check all the answers, but someone are not updated, this is how
in 2022 works whith clean code and following the Django Documentation
Here, remember that you need to make a MIGRATION to make this work:
def AvatarSave(instance, filename):
#this line changes the name of the file to the user name and put the file extension at the end after the point
return 'users/avatars/{0}.{1}'.format(instance.id,filename.split('.')[-1])
avatar = models.ImageField(_("avatar"),upload_to=AvatarSave)

Categories