Create PDFs from Django templates
Creating PDFs has been an easy task in Python due to the plenty of high-quality libraries that exist out there.
If you have tried doing it, you would be pleasantly surprised by how quickly you can create a PDF using one of those libraries. On the other hand, you would notice that most of them require drawing the elements "manually" on the page. That is defining the exact position of each element using coordinates. Even the official Django how-to section for creating PDFs mentions a way of creating PDFs using the coordinates of the consisting elements.
Yet, as web and Django developers, we are placing elements on a "page" without coordinates. We are using HTML and CSS for placing and styling components all the time. Wouldn't be great if we could create a PDF similar to how we create pages in Django? Just export the result of a template rendering into a PDF.
This is exactly what we will do in this post.
Set up
Firstly, install xhtml2pdf
that provides the HTML -> PDF functionality:
pip install xhtml2pdf
Sample code
In the following short snippet, we show how we could create an invoice PDF from a Django template. We assume that the invoice.html
template contains the layout for displaying the necessary info for an invoice, including an image logo. All the data are stored in an Invoice
model.
class InvoiceView(View):
@staticmethod
def get(request, invoice_id):
invoice = get_object_or_404(Invoice, id=invoice_id) # 1.
return _render_to_pdf(invoice)
def _render_to_pdf(invoice):
template = get_template("invoice.html")
html = template.render(
{
"logos": os.path.join(STATIC_ROOT, "logo.png"), # 2.
"invoice": invoice,
}
)
pdf = BytesIO()
pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), pdf) # 3.
res = HttpResponse(pdf.getvalue(), content_type="application/pdf")
res["Content-Disposition"] = \
f"attachment; filename=invoice{invoice.id}.pdf" # 4.
return response
- Fetch the model like you would normally do when needing some data from the DB to render a template.
- Pass to the template any context data as you would do normally. The special case here is the images that need to have an absolute path instead of a relative path. Use the
os.path.join()
method to create the absolute filesystem path of the images in your static files. For a reminder on how to set up the static files, consult the official documentation. - Use the
xhtml2pdf
to render the PDF from your template, and convert it to binary data to be downloaded in the next step. - This is how to declare the
HttpResponse
so the browser will understand that this is a binary (PDF) file that cannot be displayed in the browser but needs to be downloaded.
Hopefully, this was a quick and easy tutorial on how to create a PDF from your Django template.
Happy coding!