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

Back to Top