In this tutorial, we will generate a PDF report and serve it to the front end.
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:
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:
In the same settings.py file do not forget to add the App to the list of apps for our project
To serve the created “index.html” file in the “templates” folder, we use a simple function inside app/views.py:
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:
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:
Now the fun part starts. Create a function inside the views.py called “report”.
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:
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:
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.