I'm trying to figure how to download a word document generated with python-docx in my django app (I'm still learning and this is the first time I working with documents); with the help of ajax I send all the information needed to the view and call a function that uses that information and returns the document, then I'm trying to send this document as response in order to download it with the help of a "Download" button (or show web browser download dialog) in the same template from where I'm submitting the data, but here is where I'm stuck.
to send this document as response in order to download it with the help of a "Download" button (or show web browser download dialog) in the same template from where I'm submitting the data, but here is where I'm stuck.
What I have until now is:
1) In javascript I'm sending the information as follows:
data = { categoria: cat, familia: fam, Gcas: gcas, FI: FI, FF: FF, Test: test, Grafica: grafica }, $.ajax({ type: 'post', headers: { "X-CSRFToken": csrftoken }, url: url, data: { json_data: JSON.stringify(data) }, success: function (response) { $('#instrucciones').hide(); //Hide a div with a message $('#btndesc').show(); //Show the button to download the file generated } }); return false; }
2) In my Django view:
def Documento(request): if request.method == "GET": context={} context['form'] = catForm return render(request, 'report/report_base.html', context) if request.method == 'POST': #Data from ajax datos = request.POST.get('json_data') jsondata = json.loads(datos) Gcas = jsondata['Gcas'] FI = jsondata['FI'] FF = jsondata['FF'] grafica = jsondata['Grafica'] #Using function to create the report Reporte = ReporteWord(Gcas, FI, FF, grafica) #Response response = HttpResponse(content_type='application/vnd.openxmlformats- officedocument.wordprocessingml.document') response['Content-Disposition'] = 'attachment; filename = "Reporte.docx"' response['Content-Encoding'] = 'UTF-8' Reporte.save(response) return response
3) My function to create the document looks like:
def ReporteWord( gcas, FI, FF, Chart): #Cargamos el template template = finders.find('otros/Template_reporte.docx') document = Document(template) #Header logo = finders.find('otros/logo.png') header = document.sections[0].header paragraph = header.paragraphs[0] r = paragraph.add_run() r.add_picture(logo) #Adding title titulo = document.add_heading('', 0) titulo.add_run('Mi reporte').bold = True titulo.style.font.size=Pt(13) . Many other steps to add more content . . #IF I SAVE THE FILE NORMALLY ALL WORKS FINE #document.save(r'C:\tests\new_demo.docx') return document
I'll be very grateful for any idea or suggestion, many thanks in advance.
NOTE: I've reviewed these answers (and others) without luck.
UPDATE: Thanks to the feedback received I finally found how to generate the document and show the download dialog:
As was suggested the best way to achieve its using the view and not ajax, so the final updates in the code are:
a) Update view to work as show in feedback
b) JavaScript - Ajax control for POST method was removed and now all is handled directly with python (no extra code needed)
1) View:
def Reporte(request):
if request.method == "GET":
context={}
context['form'] = catForm
return render(request, 'reportes/reporte_base.html', context)
if request.method == 'POST':
#Getting data needed after submit the form in the page
GcasID = request.POST.get('GCASS')
FI = request.POST.get('dp1')
FF = request.POST.get('dp2')
Grafica = request.POST.get('options')
#Function to obtain complete code from GcasID
Gcas = GcasNumber(GcasID)
#Report creation
Reporte = ReporteWord(Gcas, FI, FF, Grafica)
#PART UPDATED TO SHOW DOWNLOAD REPORT DIALOG
bio = io.BytesIO()
Reporte.save(bio) # save to memory stream
bio.seek(0) # rewind the stream
response = HttpResponse(
bio.getvalue(), # use the stream's contents
content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
response["Content-Disposition"] = 'attachment; filename = "Reporte.docx"'
response["Content-Encoding"] = "UTF-8"
return response
With those changes now when I press "Create report" (submit button of form) all works as expected (as a plus no more libraries are necessary). At the end as you suggested its easier do it in this way than using ajax.
Many thanks to all for your kind help.
Python-docx's Document.save()
method accepts a stream instead of a filename. Thus, you can initialize an io.BytesIO()
object to save the document into, then dump that to the user.
Reporte = ReporteWord(Gcas, FI, FF, grafica)
bio = io.BytesIO()
Reporte.save(bio) # save to memory stream
bio.seek(0) # rewind the stream
response = HttpResponse(
bio.getvalue(), # use the stream's contents
content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
response["Content-Disposition"] = 'attachment; filename = "Reporte.docx"'
response["Content-Encoding"] = "UTF-8"
return response
This will work if you use a regular link or a form to submit the request, but since you're using $.ajax
, you may need to do additional work on the browser end to have the client download the file. It would be easier not to use $.ajax
.