0

I have a view that returns an HttpResponse:

file_name = 'rel_acao_{}.xlsx'.format(dt.now().strftime("%Y%m%d"))
response = HttpResponse(content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename={}'.format(file_name)

writer = pd.ExcelWriter(response)
df_1.to_excel(writer, sheet_name='Sheet1')
df_2.to_excel(writer, sheet_name='Sheet2')
writer.save()

return response

These view is called with the following button:

<div>
    <button id="btn_Export" type="button" onclick="sendReport()">
        Export to Excel
    </button>
</div>

which in turn, calls the following function:

// Dispatch
function sendReport(){
    $.ajax({
        url: "{% url 'report_action:report_action_csv' %}",
        type: 'POST',
        data: {
            'followup': JSON.stringify(followup),
            'report': JSON.stringify(report)
        }
    });
}

as seeing here, there is a file somewhere in this data limbo: Network data

Why am I not able to download the file? What am I doing wrong?

Onilol
  • 1,315
  • 16
  • 41
  • You know those popups that let you open or download the file? Something like that... – Onilol Jun 08 '18 at 19:19
  • That's completely different. Your current logic is serving a CSV file as the response to the AJAX request. It's possible to download a file through AJAX,but it needs to be base64 encoded and the browser needs to support Blob objects: https://stackoverflow.com/a/29939024/519413 – Rory McCrossan Jun 08 '18 at 19:20
  • you are actually downloading file, but it remains inside your js code. You should either let browser make a request, or use some browser API to present user a popup – Alexandr Tatarinov Jun 08 '18 at 19:20
  • @RoryMcCrossan I guess I could encode it on the view, but what then? – Onilol Jun 08 '18 at 19:22
  • Then you need to serve it to the browser as a Blob where it will open the Download dialog. See the answer I linked in my previous comment for code which explains how to do this - note that it is not trivial. – Rory McCrossan Jun 08 '18 at 19:23
  • actually the easiest way for you is to replace POST with GET and pass parameters as query parameters (if they are not nested and is not sensitive). Then you can completely remove your JS code and just make the button to direct browser to your url with parameters. Or you can leave the POST, wrap the button with `
    `, set parameters in a hidden fields of the form and set `action` to your url. So when button is hit, the form will be submitted and browser will process the response correctly.
    – Alexandr Tatarinov Jun 08 '18 at 19:27
  • @AlexandrTatarinov actually is a dict of dicts: `d = {'a': {...}, 'b': {...}, etc}` – Onilol Jun 08 '18 at 19:29
  • What if instead of using ajax, I dumped my data on a hidden text-area on a form and posted it with the button? Would that solve it? – Onilol Jun 08 '18 at 19:30
  • Then you have some more troubles...) Follow the BLOB way suggested by @RoryMcCrossan. Also you may have to include https://github.com/eligrey/FileSaver.js to use `saveAs`. Not sure about text-area, you will have to parse the data on back end but maybe will work. – Alexandr Tatarinov Jun 08 '18 at 19:32
  • I can try but I'm not sure on how to send the file encoded, the writer writing to response seems confusing. First timer here. – Onilol Jun 08 '18 at 19:34
  • You don't have to. Encode to BLOB on client side if you decide to stick with ajax. For form solution current response seems to be okay. Don't forget to JSON.stringify() your objects before inserting into text-area. And then you will theoretically json.loads on server-side – Alexandr Tatarinov Jun 08 '18 at 19:36

1 Answers1

1

Actually the easiest way for you is to wrap the button with <form>, set request parameters in a hidden fields of this form and set action attribute to your url. Then you can completely remove your JS code and just make the button to submit the form. So when button is hit, the form will be submitted and browser will process the response correctly and show download dialog.

Alexandr Tatarinov
  • 3,946
  • 1
  • 15
  • 30