Django2.0手册:Outputting PDFs with Django

This document explains how to output PDF files dynamically using Django views.
This is made possible by the excellent, open-source ReportLab Python PDF
library.

The advantage of generating PDF files dynamically is that you can create
customized PDFs for different purposes — say, for different users or different
pieces of content.

For example, Django was used at kusports.com to generate customized,
printer-friendly NCAA tournament brackets, as PDF files, for people
participating in a March Madness contest.

Install ReportLab¶

The ReportLab library is available on PyPI. A user guide (not
coincidentally, a PDF file) is also available for download.
You can install ReportLab with pip:

$ pip install reportlab

Test your installation by importing it in the Python interactive interpreter:

>>> import reportlab

If that command doesn’t raise any errors, the installation worked.

Write your view¶

The key to generating PDFs dynamically with Django is that the ReportLab API
acts on file-like objects, and Django’s HttpResponse
objects are file-like objects.

Here’s a “Hello World” example:

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

def some_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='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.
    p.showPage()
    p.save()
    return response

The code and comments should be self-explanatory, but a few things deserve a
mention:

  • The response gets a special MIME type, application/pdf. This
    tells browsers that the document is a PDF file, rather than an HTML file.
    If you leave this off, browsers will probably interpret the output as
    HTML, which would result in ugly, scary gobbledygook in the browser
    window.

  • The response gets an additional Content-Disposition header, which
    contains the name of the PDF file. This filename is arbitrary: Call it
    whatever you want. It’ll be used by browsers in the “Save as…” dialog, etc.

  • The Content-Disposition header starts with 'attachment; ' in this
    example. This forces Web browsers to pop-up a dialog box
    prompting/confirming how to handle the document even if a default is set
    on the machine. If you leave off 'attachment;', browsers will handle
    the PDF using whatever program/plugin they’ve been configured to use for
    PDFs. Here’s what that code would look like:

    response['Content-Disposition'] = 'filename="somefilename.pdf"'
    
  • Hooking into the ReportLab API is easy: Just pass response as the
    first argument to canvas.Canvas. The Canvas class expects a
    file-like object, and HttpResponse objects fit the
    bill.

  • Note that all subsequent PDF-generation methods are called on the PDF
    object (in this case, p) — not on response.

  • Finally, it’s important to call showPage() and save() on the PDF
    file.

Note

ReportLab is not thread-safe. Some of our users have reported odd issues
with building PDF-generating Django views that are accessed by many people
at the same time.

Complex PDFs¶

If you’re creating a complex PDF document with ReportLab, consider using the
io library as a temporary holding place for your PDF file. This
library provides a file-like object interface that is particularly efficient.
Here’s the above “Hello World” example rewritten to use io:

from io import BytesIO
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(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'

    buffer = BytesIO()

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

    # 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.
    p.showPage()
    p.save()

    # Get the value of the BytesIO buffer and write it to the response.
    pdf = buffer.getvalue()
    buffer.close()
    response.write(pdf)
    return response

Other formats¶

Notice that there isn’t a lot in these examples that’s PDF-specific — just the
bits using reportlab. You can use a similar technique to generate any
arbitrary format that you can find a Python library for. Also see
Outputting CSV with Django for another example and some techniques you can use
when generated text-based formats.

See also

Django Packages provides a comparison of packages that help generate PDF files
from Django.