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>
<a href="http://localhost/toollistvscalm/download/">Download</a>
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
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