Freelancing for Pale Blue

Looking for flexible work opportunities that fit your schedule?


Create PDFs from Django templates

Django Jan 21, 2023

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
  1. Fetch the model like you would normally do when needing some data from the DB to render a template.
  2. 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.
  3. Use the xhtml2pdf to render the PDF from your template, and convert it to binary data to be downloaded in the next step.
  4. This is how to declare the HttpResponseso 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!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.