Как позволить пользователю скачать файл после завершения процесса в Django?

Я начинающий в Django. Я пытаюсь позволить пользователю скачать файл после завершения определенного процесса.

Здесь view.py. После завершения процесса отображается кнопка загрузки. Пользователи могут загрузить файл с именем WS_имя_файла+'.xlsx', нажав на кнопку загрузки.

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)

Ниже приведен код template_Form.html. Эта страница позволяет пользователю заполнить информацию, которая используется для обработки файла.

<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>

Ниже приведен код template_Download.html. Эта страница отображается после завершения процесса. Кнопка загрузки находится прямо на этой странице.

<h3>Hi, {{username}} your toollist {{version}} vs CALM report is completed.</h3>
<a href="http://localhost/toollistvscalm/download/">Download</a>

Ниже приведен код urls.py, который используется для вызова функций в views.py.

urlpatterns = [
    path('admin/', admin.site.urls),
    path('toollistvscalm/', views.index),
    path('toollistvscalm/download/', views.download_file),
]

После нажатия на кнопку загрузки появляется следующая ошибка.

name 'WS_file_name' is not defined

Я перепробовал все, чтобы исправить это, но тщетно. Пожалуйста, помогите мне.

Ок, проблема в том, что в вашей функции download_file переменная WS_file_name не определена. Поскольку функция download_file не может взаимодействовать с функцией index (по крайней мере, так, как вы ее настроили), вам нужно заполнить переменную WS_file_name точно таким же именем файла, которое вы дали ей, когда сохраняли файл в функции index.

Тогда все будет хорошо!

Приведу пример с жестко заданными именами файлов, которые должны работать:

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)

Но учтите следующее: Это требует, чтобы вы хранили все эти файлы на вашей хост-машине. Мне кажется, что вам следует перенести раздел ### Processing ### в функцию download_file. Таким образом, вам не нужно будет хранить файл на вашей машине, и вы сможете представить его пользователю "на лету".

Редактирование:

Поскольку ОП хочет динамические имена файлов - вот вам...

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)

Но все же я не считаю это хорошим вариантом, так как файлы excel будут загрязнять файловую систему и хранилище вашей хост-машины

Вернуться на верх