Copy file into another folder with django? - python

I need to upload profile images into diferent folders in Django. So, I have a folder for each account, and the profile image have to go to the specific folder. How can I do that?
Here is my uploadprofile.html
<form action="{% url 'uploadimage' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
<input type="file" name="avatar" accept="image/gif, image/jpeg, image/png">
<button type="submit">Upload</button>
</form>
And here is my view in views.py
def uploadimage(request):
img = request.FILES['avatar'] #Here I get the file name, THIS WORKS
#Here is where I create the folder to the specified profile using the user id, THIS WORKS TOO
if not os.path.exists('static/profile/' + str(request.session['user_id'])):
os.mkdir('static/profile/' + str(request.session['user_id']))
#Here is where I create the name of the path to save as a VARCHAR field, THIS WORKS TOO
avatar = "../../static/profile/" + str(request.session['user_id']) + "/" + str(img)
#THEN I HAVE TO COPY THE FILE IN img TO THE CREATED FOLDER
return redirect(request, 'myapp/upload.html')

You can pass a callable to upload_to. Basically, what it means is whatever value that callable returns, the image will be uploaded in that path.
Example:
def get_upload_path(instance, filename):
return "%s/%s" % (instance.user.id, filename)
class MyModel:
user = ...
image = models.FileField(upload_to=get_upload_path)
There's more information in the docs and an example too, though similar to what I posted above.

By looking at Django docs what you get when you do img = request.FILES['avatar'] you get a file descriptor that points to an open file with your image.
Then you should to dump the contents in your actual avatar path, right?
#Here is where I create the name of the path to save as a VARCHAR field, THIS WORKS TOO
avatar = "../../static/profile/" + str(request.session['user_id']) + "/" + str(img)
# # # # #
with open(avatar, 'wb') as actual_file:
actual_file.write(img.read())
# # # # #
return redirect(request, 'myapp/upload.html')
Beware: the code is untested.

from django.shortcuts import render
from django.conf import settings
from django.core.files.storage import FileSystemStorage
def uploadimage(request):
if request.method == 'POST' and request.FILES['avatar']:
img = request.FILES['avatar']
fs = FileSystemStorage()
#To copy image to the base folder
#filename = fs.save(img.name, img)
#To save in a specified folder
filename = fs.save('static/profile/'+img.name, img)
uploaded_file_url = fs.url(filename) #To get the file`s url
return render(request, 'myapp/upload.html', {
'uploaded_file_url': uploaded_file_url
})
return render(request, 'myapp/upload.html')

Related

How to let user download a file after the process is completed in Django?

I am a beginner in Django. I am trying to let user download a file after the specific process is completed.
Here is view.py. The download button is shown after the process is completed. Users can download the file named WS_file_name+'.xlsx' by clicking the download button.
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
if request.method == 'POST':
student = StudentForm(request.POST, request.FILES)
if student.is_valid():
handle_uploaded_file(request.FILES['file'])
firstname= student.cleaned_data.get("firstname")
lastname= student.cleaned_data.get("lastname")
### Processing ###
WS_file_name = lastname + firstname + newfile
Toollist_Raw = pd.read_excel(Toollist_path+Toollist_file_name)
WS_file = xlsxwriter.Workbook(WS_file_name+'.xlsx')
WS_file.close()
file_source = WS_Path + WS_file_name+'.xlsx'
Toollist_Raw.to_excel(file_source, sheet_name='CALM_Toollist',index=False)
### Process had completed, users can click download button to download the file ###
context= {'username': firstname, 'version':lastname,}
return render(request, 'template_Download.html', context)
else:
student = StudentForm()
return render(request,"template_Form.html",{'form':student})
##### Download Functions #####
import os
from django.http import FileResponse
def download_file(request):
# Define Django project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Define file name
filename = WS_file_name+'.xlsx'
# Define the full file path
filepath = BASE_DIR + '/Capital_Report_Website/Download_Files/Baseline_Cleanup_Toollist_vs_CALM_Download/' + filename +'.xlsx'
return FileResponse(open(filepath, 'rb'), as_attachment=True)
The below code is template_Form.html. This page is to let user fill in the information which is used to process the file.
<form method="POST" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Generate Report</button>
</form>
The below code is template_Download.html. This page is shown after the process is completed. The download button is right on this page.
<h3>Hi, {{username}} your toollist {{version}} vs CALM report is completed.</h3>
Download
The below code is urls.py which is used to call the functions in views.py.
urlpatterns = [
path('admin/', admin.site.urls),
path('toollistvscalm/', views.index),
path('toollistvscalm/download/', views.download_file),
]
It showed the below error after I clicked download button.
name 'WS_file_name' is not defined
After all, the whole process I want is as follows.
Users fill in lastname, firstname and upload the file newfile in form.
Django get newfile and do the process to create a new file (the file name have to be WS_file_name+'.xlsx') in backend.
After the process completed, it shows the button for users to download the file WS_file_name+'.xlsx'.
Users click the button to download the file WS_file_name+'.xlsx'.
I had tried everything to fix it but just in vain. Please help me.
Ok, so the problem is, that in your function download_file the variable WS_file_name is not defined.
As function download_file can not interact with function index (at least the way you set it up) you need to fill that variable WS_file_name with exactly the same filename you gave it when you stored the file in function index.
Then you should be fine!
I will give you an example with hardcoded filenames that should work:
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
if request.method == 'POST':
student = StudentForm(request.POST, request.FILES)
if student.is_valid():
handle_uploaded_file(request.FILES['file'])
firstname= student.cleaned_data.get("firstname")
lastname= student.cleaned_data.get("lastname")
### Processing ###
WS_file_name = "hardcoded_filename" # HERE FILENAME
Toollist_Raw = pd.read_excel(Toollist_path+Toollist_file_name)
WS_file = xlsxwriter.Workbook(WS_file_name+'.xlsx')
WS_file.close()
file_source = WS_Path + WS_file_name+'.xlsx'
Toollist_Raw.to_excel(file_source, sheet_name='CALM_Toollist',index=False)
### Process had completed, users can click download button to download the file ###
context= {'username': firstname, 'version':lastname,}
return render(request, 'template_Download.html', context)
else:
student = StudentForm()
return render(request,"template_Form.html",{'form':student})
##### Download Functions #####
import os
from django.http import FileResponse
def download_file(request):
# Define Django project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Define file name
filename = "hardcoded_filename" +'.xlsx' # HERE SAME FILENAME
# Define the full file path
filepath = BASE_DIR + '/Capital_Report_Website/Download_Files/Baseline_Cleanup_Toollist_vs_CALM_Download/' + filename +'.xlsx'
return FileResponse(open(filepath, 'rb'), as_attachment=True)
But consider the following:
This requires you to store all those files on your host-machine. I feel like you should move the ### Processing ### section to the download_file function. Therefore you would not need to store the file on your machine and you can present it to the user "on the fly".
Edit:
As OP wants dynamic filenames - there you go...
models.py
class Student(models.Model):
firstname = models.CharField(max_lenght=30)
lastname = models.CharField(max_lenght=30)
path_to_xlsx = models.FilePathField(null=True, blank=True) # don't know if this works, or just put a default in.
views.py
def index(request):
[...]
if student.is_valid():
the_student = Student.objects.get(pk=<your-student>) # somehow grab your student here
[... file processing ...]
the_student.path_to_xlsx = os.path.join(path_to_your_file)
the_student.save()
the_student = student.save()
def download_file(request):
[...]
the_student = Student.objects.get(pk=<your-student>) # somehow grab your student here
return FileResponse(open(the_student.path_to_xlsx, 'rb'), as_attachment=True)
But still I do not consider this as good, as the excel files will pollute the filesystem and storage of your host machine

How can I add image file in SQLite database using Flask, Python

I'm writing a small web-shop using Python and web-framework Flask.
I'm trying to add image file in SQLite database using this
#app.route('/new_product_reg', methods=['POST', 'GET'])
def new_product_reg():
if request.method == 'POST':
img_name = request.form['product_img']
product_img = readImage(img_name)
product_img_binary = lite.Binary(product_img)
product_data = NewProduct(product_img=product_img_binary)
try:
db.session.add(product_data)
db.session.commit()
return redirect('/new_product_reg')
except:
return "ERROR!"
Where readImage is
def readImage(img_name):
try:
fin = open(img_name, 'rb')
img = fin.read()
return img
except:
print("ERROR!!")
Form where I taked the image:
<form enctype="multipart/form-data" method="post">
<input type="file" name=product_img id="product_img"><br>
<input type="submit" value="Submit">
</from>
And the class of database where I want to add image looks like that:
class NewProduct(db.Model):
product_img = db.Column(db.BLOB())
def __repr__(self):
return '<NewProduct %r>' % self.id
So, the problem is, when I added image by pressing "Add Image" button in form and pressed "Submit" button I get the BadRequestKeyError 400. The debugger said that the problem is in img_name = request.form['product_img']. So how can I fix that and what I'm doing wrong?
At first I want to say that storing large and medium pictures directly in SQLite is not a very good solution. Learn more here: https://www.sqlite.org/intern-v-extern-blob.html
For your problem, try to do as in the documentation:
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/
Note there is used:
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
And perhaps this article will also help you:
Python SQLite BLOB to Insert and Retrieve file and images

Django sorl: not enough values to unpack (expected 2, got 1)

I am trying to have a form to generate thumbnails from images that will be uploaded
I will be using sorl for the thumb generation and I am following the following documentation:
Django multiple file upload: https://docs.djangoproject.com/en/3.0/topics/http/file-uploads/
Sorl Low level API: https://sorl-thumbnail.readthedocs.io/en/latest/examples.html
When I try to generate the thumbnail I get the error of
not enough values to unpack (expected 2, got 1)
I dont understand what I am doing wrong, in summary I upload the image and this get saved in my root directory, then im trying to create the thumb
Also is there a way to void saving this original image in the root? I am planning to send both Image and thumb to google cloud storage
My forms.py:
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
My html file: upload.html
<html>
<head></head>
<body>
<h3>Read File Content</h3>
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
</body>
</html>
My views.py looks like:
from sorl.thumbnail import ImageField, get_thumbnail
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'app_workflow/upload.html' # Replace with your template.
success_url = '/photo' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
with open(f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
im = get_thumbnail(f.name, '100x100', crop='center', quality=99)
return self.form_valid(form)
else:
return self.form_invalid(form)
As you have said in the question, you do not want to store in root and generate thumbnail. Then I would suggest something like this:
from PIL import Image
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'app_workflow/upload.html' # Replace with your template.
success_url = '/photo' # Replace with your URL or reverse().
def form_valid(self, *args, **kwargs):
img_size = (100, 100)
files = self.request.FILES.getlist('file_field')
for f in files:
im = Image.open(f)
im.thumbnail(img_size)
# your thumbnail image is in memory now
# you can now store it in your model and use django-storages to upload it to gcloud
return super().form_valid(*args, **kwargs)
Here I am not storing images and directly loading it in the PIL.Image module to generate thumbnails. You can use django-storages to upload data from FileField to gcloud.
Storing in root:
Then you can change the code like this:
for f in files:
for chunk in f.chunks():
destination.write(chunk)
im = Image.open(f)
im.thumbnail(img_size)
im.save('thumb_{}'.format(f.name))

How can I store a file path in Mysql database using django?

I need to store a file path in db using django via ajax form submission .
Here is my view:
def dashboard(request):
container=[]
DIR = os.path.realpath("/home/user/Desktop/Demo")
WAY = os.listdir(DIR)
for file in WAY:
if file.endswith('.mp4'):
file_name = file
FDIR=os.path.join(DIR, file)
container.append(FDIR)
return render(request, 'dashboard.html', {'container': container})
def new_scheduler(request):
if request.method =='POST':
f_name = request.POST.get('file')
dateAndTime = request.POST.get('dateAndTime')
Scheduled_data = schedulesdb.objects.create(
f_name = file,
dateAndTime = dateAndTime,
)
Scheduled_data.save()
return HttpResponse ('done')
It save in database like <type 'file'> .
Here is my model.py:
class schedulesdb(models.Model):
f_name = models.CharField(max_length=100)
dateAndTime = models.DateTimeField(['%Y-%m-%d %H:%M:%S'],null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=2)
def __unicode__(self): # on Python 2
return self.f_name
Thanks in advance :)
From your code it's not 100% clear whether you're intending to handle file uploads from the client, or simply store strings that happen to be a file path (potentially for locating a file on some remote filesystem).
1. File uploads
Consider using the FileField model field type rather than the CharField.
The Django documentation has a solid explanation and examples of how to do simple file uploads.
2. Obtaining the actual POST data value for the f_name field
Your code sample is storing "", because you're assigning 'file' (which is a builtin type) rather than the f_name variable that you previously declared. Like this:
def new_scheduler(request):
if request.method =='POST':
f_name = request.POST.get('file')
dateAndTime = request.POST.get('dateAndTime')
Scheduled_data = schedulesdb.objects.create(
f_name = f_name, # Note the use of f_name instead of file
dateAndTime = dateAndTime,
)
Scheduled_data.save()
return HttpResponse ('done')
I haven't even used ajax, but I stored file path in mysql and stored file in media folder in django
Here's my views.py:
from CRUDoperation.models import EmpModel
def upload(request):
if request.method == 'POST':
saverecord=EmpModel()
myfile = request.FILES['myfile']
fs = FileSystemStorage()
filename = fs.save(myfile.name, myfile)
uploaded_file_url = fs.url(filename)
saverecord.file=uploaded_file_url
return render(request,'upload.html')
models.py file
class EmpModel(models.Model):
file=models.CharField(max_length=150)
class Meta:
db_table="employee"
settings.py file
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
my html code:
{% load static %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="myfile">
<button type="submit">Upload</button>
</form>

Django download a file

I'm quite new to using Django and I am trying to develop a website where the user is able to upload a number of excel files, these files are then stored in a media folder Webproject/project/media.
def upload(request):
if request.POST:
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return render_to_response('project/upload_successful.html')
else:
form = FileForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('project/create.html', args)
The document is then displayed in a list along with any other document they have uploaded, which you can click into and it will displays basic info about them and the name of the excelfile they have uploaded. From here I want to be able to download the same excel file again using the link:
Download Document
My urls are
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date")[:25],
template_name="project/project.html")),
url(r'^(?P<pk>\d+)$', DetailView.as_view(model=Post, template_name="project/post.html")),
url(r'^upload/$', upload),
url(r'^download/(?P<path>.*)$', serve, {'document root': settings.MEDIA_ROOT}),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
but I get the error, serve() got an unexpected keyword argument 'document root'. can anyone explain how to fix this?
OR
Explain how I can get the uploaded files to to be selected and served using
def download(request):
file_name = #get the filename of desired excel file
path_to_file = #get the path of desired excel file
response = HttpResponse(mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
return response
You missed underscore in argument document_root. But it's bad idea to use serve in production. Use something like this instead:
import os
from django.conf import settings
from django.http import HttpResponse, Http404
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
You can add "download" attribute inside your tag to download files.
<a href="/project/download" download> Download Document </a>
https://www.w3schools.com/tags/att_a_download.asp
Reference:
In view.py Implement function like,
def download(request, id):
obj = your_model_name.objects.get(id=id)
filename = obj.model_attribute_name.path
response = FileResponse(open(filename, 'rb'))
return response
When you upload a file using FileField, the file will have a URL that you can use to point to the file and use HTML download attribute to download that file you can simply do this.
models.py
The model.py looks like this
class CsvFile(models.Model):
csv_file = models.FileField(upload_to='documents')
views.py
#csv upload
class CsvUploadView(generic.CreateView):
model = CsvFile
fields = ['csv_file']
template_name = 'upload.html'
#csv download
class CsvDownloadView(generic.ListView):
model = CsvFile
fields = ['csv_file']
template_name = 'download.html'
Then in your templates.
#Upload template
upload.html
<div class="container">
<form action="#" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button class="btn btn-primary btn-sm" type="submit">Upload</button>
</form>
#download template
download.html
{% for document in object_list %}
Download
{% endfor %}
I did not use forms, just rendered model but either way, FileField is there and it will work the same.
I've found Django's FileField to be really helpful for letting users upload and download files. The Django documentation has a section on managing files. You can store some information about the file in a table, along with a FileField that points to the file itself. Then you can list the available files by searching the table.
#Biswadp's solution worked greatly for me
In your static folder, make sure to have the desired files you would like the user to download
In your HTML template, your code should look like this :
Download
Using the below approach makes everything less secure since any user can access any user's file.
<a href="/project/download" download> Download Document </a>
Using the below approach makes no sense since Django only handles one requests at the time (unless you are using gunicorn or something else), and believe me, the below approach takes a lot of time to complete.
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
So what is the optimum solution?
Use Nginx authenticated routes. When requesting a file from Nginx you can make a request to a route and depending on the HTTP response Nginx allows to denies that request. This makes it very secure and also scalable and performant.
You can ready about more here
import mimetypes
from django.http import HttpResponse, Http404
mime_type, _ = mimetypes.guess_type(json_file_path)
if os.path.exists(json_file_path):
with open(json_file_path, 'r') as fh:
response = HttpResponse(fh, content_type=mime_type)
response['Content-Disposition'] = "attachment; filename=%s" % 'config.json'
return response
raise Http404
<a href='/your-download-view/' download>Download</a>
In your view:
from django.http import FileResponse
def download(request):
# pre-processing, authorizations, etc.
# ...
return FileResponse(open(path_to_file, 'rb'), as_attachment=True)
Simple using html like this downloads the file mentioned using static keyword
Download CV
1.settings.py:
MEDIA_DIR = os.path.join(BASE_DIR,'media')
#Media
MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = '/media/'
2.urls.py:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
3.in template:
<a href="{{ file.url }}" download>Download File.</a>
Work and test in django >=3
for more detail use this link:
https://youtu.be/MpDZ34mEJ5Y
If the file is a FileField in the model, this is the way I do it:
try:
download_file = PrintingFile.objects.get(pk=kwargs.get('pk_file', 0))
return FileResponse(download_file.file.open(), as_attachment=True)
except PrintingFile.DoesNotExist:
raise Http404
More here
I use this method:
{% if quote.myfile %}
<div class="">
<a role="button"
href="{{ quote.myfile.url }}"
download="{{ quote.myfile.url }}"
class="btn btn-light text-dark ml-0">
Download attachment
</a>
</div>
{% endif %}
If you hafe upload your file in media than:
media
example-input-file.txt
views.py
def download_csv(request):
file_path = os.path.join(settings.MEDIA_ROOT, 'example-input-file.txt')
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
urls.py
path('download_csv/', views.download_csv, name='download_csv'),
download.html
a href="{% url 'download_csv' %}" download=""

Categories

Resources