Thought leadership from the most innovative tech companies, all in one place.

How to Generate and Serve PDF Files with Django

How to generate PDF reports in Django

In this tutorial, we will generate a PDF report and serve it to the front end.

image

Create a simple Django Project

We start by creating a Django project:

// create a Django project named pdfreport:
django-admin startproject pdfreport
// go to the new folder
cd pdfreport
// create a new environment
python3 -m venv venv
// activate this environment
source ./venv/bin/activate
// create an App for our project
python manage.py startapp app
// Install Django and FPDF2 in this environment
pip install django
pip install fpdf2

Open this folder in your IDE and create some initial functions.

First, we want to use HTML templates, so in the app, we create a folder called “templates” and an empty “index.html” file:

image

Now, we want to tell Django where to look for the templates. Go to the settings.py file and add this string to the templates DIR list:

image

In the same settings.py file do not forget to add the App to the list of apps for our project

image

To serve the created “index.html” file in the “templates” folder, we use a simple function inside app/views.py:

image

Now, we add simple HTML content to the index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>PDF Report</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
</head>
<body>
    <div class="container shadow"
         style="max-width: 640px; margin: auto; margin-top: 2em; padding: 2em">
        <h4>To generate a report, please click the button below:</h4>
        <a class="btn btn-success" href="/report">Report</a>
    </div>
</body>
</html>

Here we add a bootstrap, which is overkill, I know, but I always do when creating a new HTML file. Then we add a small message and a button.

If you run your project, you should see this:

image

Working on the report

Please note that we refer to “/report” URL inside the green “Report” button. Go to urls.py and add this URL:

image

Now the fun part starts. Create a function inside the views.py called “report”.

image

To create the PDF file we need to read the official documentation first. You may find it here: https://pyfpdf.readthedocs.io/en/latest/Tutorial/index.html

I have taken some time and read the above docs. In the end, I came up with this simple function:

def report(request):
    sales = [
        {"item": "Keyboard", "amount": "$120,00"},
        {"item": "Mouse", "amount": "$10,00"},
        {"item": "House", "amount": "$1 000 000,00"},
    ]
    pdf = FPDF('P', 'mm', 'A4')
    pdf.add_page()
    pdf.set_font('courier', 'B', 16)
    pdf.cell(40, 10, 'This is what you have sold this month so far:',0,1)
    pdf.cell(40, 10, '',0,1)
    pdf.set_font('courier', '', 12)
    pdf.cell(200, 8, f"{'Item'.ljust(30)} {'Amount'.rjust(20)}", 0, 1)
    pdf.line(10, 30, 150, 30)
    pdf.line(10, 38, 150, 38)
    for line in sales:
        pdf.cell(200, 8, f"{line['item'].ljust(30)} {line['amount'].rjust(20)}", 0, 1)

    pdf.output('tuto1.pdf', 'F')
    return render(request, "index.html")

that generates a PDF like this:

image

If you run the project, you should be able to press the green “Report” button. Nothing will happen on the front end. But a new file should be created in your project:

image

Serve this file to the Front End

In order to return this generated file to the user, we need to return a “FileResponse”, not a “render”.

First, import it:

from django.http import FileResponse

and then return it:

return FileResponse(open('report.pdf', 'rb'), as_attachment=True,
content_type='application/pdf')

This is how the “views.py” file looks like in the end:

from django.shortcuts import render

from django.http import FileResponse

from fpdf import  FPDF




def  index(request):

context = {}

return render(request, "index.html", context=context)




def  report(request):

sales = [

{"item": "Keyboard", "amount": "$120,00"},

{"item": "Mouse", "amount": "$10,00"},

{"item": "House", "amount": "$1 000 000,00"},

]

pdf = FPDF('P', 'mm', 'A4')

pdf.add_page()

pdf.set_font('courier', 'B', 16)

pdf.cell(40, 10, 'This is what you have sold this month so far:',0,1)

pdf.cell(40, 10, '',0,1)

pdf.set_font('courier', '', 12)

pdf.cell(200, 8, f"{'Item'.ljust(30)}  {'Amount'.rjust(20)}", 0, 1)

pdf.line(10, 30, 150, 30)

pdf.line(10, 38, 150, 38)

for line in sales:

pdf.cell(200, 8, f"{line['item'].ljust(30)}  {line['amount'].rjust(20)}", 0, 1)

pdf.output('report.pdf', 'F')

return FileResponse(open('report.pdf', 'rb'), as_attachment=True, content_type='application/pdf')

If you don’t want the file to be downloaded, but rather open in the browser, then remove the “as_attachment” part or set it to False.




Continue Learning