Generating PDFs with Django

By : Shabda Raaj

If your web app creates report chances are you also want this report in PDF form. The Django docs describe a way to generate PDFs using ReportLab. Here is some code from there.

from reportlab.pdfgen import canvas
from django.http import HttpResponse

def some_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'

    # Create the PDF object, using the response object as its "file."
    p = canvas.Canvas(response)

    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    p.drawString(100, 100, "Hello world.")

    # Close the PDF object cleanly, and we're done.
    return response

This suffers from two problems,

  1. You are laying out your PDF using Python, which means if you later want to change the design of the PDF you need to change the Python code.
  2. Most of the time you already have the report in Html, form, writing the same PDF via ReportLab is error prone.

Both these problems can be cleanly solved using Pisa, a Html2Pdf library. We proceed as,

  1. Generate a Html representation of Pdf using normal Django macienry.
  2. Convert to Pdf using Pisa.
  3. Return PDF.

This solves both our problems as,

  1. Designers can edit the template to change the layout of Pdf.
  2. The code to generate the Html and Pdf views can share code.

Here is some example code.

def html_view(request, as_pdf = False):
    #Get varaibles to populate the template
    payload = {'data':data, ....}
    if as_pdf:
        return payload
    return render_to_response('app/template.html', payload, RequestContext(request))

def pdf_view(request):
    payload = html_view(request, as_pdf = True)
    file_data = render_to_string('app/template.pdf', payload, RequestContext(request))
    myfile = StringIO.StringIO()
    pisa.CreatePDF(file_data, myfile)
    response =  HttpResponse(myfile, mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=coupon.pdf'
    return response

Related Posts

Can we help you build amazing apps? Contact us today.

Topics : tips


Joshua Jonah

So it's generating the PDF from the HTML representation. Are there any pitfalls I should know about?

Arne Brodowski

Joshua: I documented some pitfalls I ran into on my blog. Except for these I am using pisa very successfully.

Paul 6th Nov., 2008

I ran into a problem with the images. I don't really understand what you mean by generating a call back function?
Is it possible that you could give some clarity or code on that so that I understand how this relates in a django view? Thank you.

Piotr Plenik

Small correction:
response = HttpResponse(myfile, mimetype='application/pdf')
response = HttpResponse(myfile.getvalue(), mimetype='application/pdf')


I think template.pdf should be template.html in function pdf_view -

render_to_string('app/template.pdf', payload, RequestContext(request))

Can you confirm?

vivek 17th Jan., 2013

the whole function is working for me with a little change
render_to_string('app/template.html', payload, RequestContext(request)),
but the pdf is not having css(proper layout)

i have also write func

def fetch_resources(uri, rel):
path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ""))
print path
return path

and my main function is:

def generate_pdf(request):
payload = generate(request,as_pdf=True)
file_data = render_to_string('translate/order.html', payload, RequestContext(request))
myfile = StringIO.StringIO()
pisa.CreatePDF(file_data, myfile,link_callback=fetch_resources)
response = HttpResponse(myfile.getvalue(), mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=invoice.pdf'
return response


© Agiliq, 2009-2012