1

I have a views.py that:

  • creates some .xlsx files
  • select the correct .zip and place the file inside

After that, I want this .zip to be automatically downloaded. I did some research and tested some codes but none worked.

I created a "temp" folder in the root of the app where the created files are stored.

simplified view.py

def generate_ws(request,cource,ca_id):
    ca = get_object_or_404(CreditAnalysis,pk=ca_id)
    ca_owners = CAOwner.objects.filter(ca_operation=ca)
    mo_farms = MOFarm.objects.filter(ca_operation=ca)
    misses = []

    generate_owner_mo(ca_owner,misses,city)
    zip_name = 'temp/MOs - ' + str(ca_owner.owner) + '.zip'
    zf = zipfile.ZipFile(zip_name,'w')
    zf.close()

    generate_farm_mo(mo_farm,misses,city)
    generate_production_mo(ca,misses,city,production_city,pks)

    files = glob.glob('temp/*.xlsx')       #SELECT FILES AND PUT IN .ZIP
    for file in files:
        file_key = file.split('.')[0]
        file_key=file_key.split(' - ')
        for ca_owner in ca_owners:
            zip_name = 'temp/MOs - ' + str(ca_owner.owner) + '.zip'
            if str(ca_owner.owner) in file_key:
                zf = zipfile.ZipFile(zip_name,'a')
                new_file_name = file[5:]
                zf.write(file,new_file_name)
                zf.close()                
                break
     files = glob.glob('temp/*.zip')             # GET .ZIP FILES
     for file in files:
         download_mo(request,file)               # CREATE A DOWNLOAD FOR EACH .ZIP FILE

    misses = list(set(misses))

    return render(request,'generate_mo.html',{'misses':misses,})

download_mo

def download_mo(request,file):
    path_to_file = os.path.realpath(file)
    with open(path_to_file,'rb') as fh:
        response = HttpResponse(fh.read())
        file_name = file[5:]                       #WITHDRAW "temp/"
        response['Content-Disposition'] = 'inline; filename=' + file_name
        return response

Everything works correctly except the download which never starts

1 Answers1

2

In order to download a file, you need to return a FileResponse to the user. However, calling an external function that returns a FileResponse won't work because you're not actually returning the FileResponse to the user, in your case, the user only receives the render(request, 'generate_mo.html', {'misses':misses,}) so that won't download the files.

You can't download several files one after the other, so I suggest putting them all in a .zip or .tar file so that you can download them as only one file, and only need to return one FileResponse.

As you also need to render your template, you can redirect to your download_mo view on template loading so that your file is downloaded while your template is rendered.

Now, for your download_mo view, replace your HttpResponse with a FileResponse :

from django.http import FileResponse
def download_mo(request,file):
    path_to_file = os.path.realpath(file)
    response = FileResponse(open(path_to_file, 'rb'))
    file_name = file[5:]
    response['Content-Disposition'] = 'inline; filename=' + file_name
    return response
Mark Wasfy
  • 167
  • 1
  • 13
Balizok
  • 904
  • 2
  • 5
  • 19
  • it still didn't work – Luiz Henrique Rochelle Jun 29 '22 at 13:59
  • I modified my answer, it should work now. – Balizok Jun 29 '22 at 14:14
  • I think u forget "response =" in second line. But, still not working – Luiz Henrique Rochelle Jun 29 '22 at 14:37
  • I've thought about it, and even though I didn't find a way to do what you need, I found some interesting things. First, you can't download a file by just calling a external function, you must return a FileResponse to the user. So basically, you can't download multiple files one after the others. The only way to download multiple files would be to put all of them in a .zip or .tar file, but I don't know if that would be possible. – Balizok Jul 02 '22 at 10:00
  • I succeeded in downloading a single file as well as returning your `generate_mo.html` template by redirecting to the `download_mo` function on template loading, but couldn't do it for several files. I don't know if that can help you, but I didn't manage to do better than that. You can also put a download button on your template which calls your `download_mo` function, but once more I don't know if that can be useful to you. – Balizok Jul 02 '22 at 10:02
  • It's okay for me to merge them all in a .zip. Downloading not happening because FileResponse is in an external function? – Luiz Henrique Rochelle Jul 04 '22 at 12:39
  • can you explain me better the part where you explain how to download and still render another page? I've tried everything here and it didn't work. – Luiz Henrique Rochelle Jul 06 '22 at 00:33
  • You can either render your template, and on it you can have a link to redirect to your download url, however that requires the user to click the link. The other way is to render your template, and when it's loaded, you redirect to your download url. See [this question](https://stackoverflow.com/questions/72802359/django-view-to-download-a-file-from-server/72802532?noredirect=1#comment128719735_72802532) for how to do it. I suggest still putting a download link in case the automatic download fails. – Balizok Jul 06 '22 at 07:12
  • I think you made a mistake when linking "this question", it is redirecting to this same question. – Luiz Henrique Rochelle Jul 06 '22 at 12:13
  • Indeed, I wanted to put a link for [this one](https://stackoverflow.com/questions/46498522/html-redirect-on-page-load)... – Balizok Jul 06 '22 at 12:43