How To Generate a PDF in Django

Evenword
5 min readAug 29, 2023

--

Generate a PDF from HTML in your Django project with xhtml2pdf

While working on a personal Django project I wanted to add a feature where users can download some information as a PDF file. I did it with a library called xhtml2pdf.

In this article, I will discuss with you how to generate PDFs from a Django website using xhtml2pdf. Let’s get started.

Table of Contents:· Project Setup
· Installing Necessary Libraries
· Creating the User model
· Populating the Database With Dummy Data
· Creating Views
· Creating Templates
∘ base.html
∘ main.html
∘ generate_pdf.html
· Creating URLs
· Conclusion

Project Setup

First of all, let’s create a Django project called pdf_proj.

$ django-admin startproject pdf_proj

It will be a simple project just to demonstrate the process of creating PDFs from HTML so that you can use it as a future reference.

Inside the project, we will create an app named users.

$ python manage.py startapp users

The users app is to create users for our project. Where we will populate our database with some dummy user data. Then we will create PDF files from that data.

We will also show images in our PDF files. So we need to handle static and media files in our project. Let’s create two folders called static and media in our project root directory. The static images will be stored in the static folder and images uploaded by users will be stored in the media folder.

Here is the project structure so far:

├── manage.py
├── media
├── pdf_proj
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-39.pyc
│ │ └── settings.cpython-39.pyc
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── static
└── users
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py

Now we will install the users app in the settings.py file. And also we will add STATIC_ROOT and MEDIA_ROOT.

Installing the users app

STATIC_URL and MEDIA_URL

Then we will open the urls.py file and setup STATIC_URL and MEDIA_URL with the urlpatterns.

And after that, we will create a superuser so that we can access the admin dashboard.

$ python manage.py createsuperuser

The initial project setup is done. Now we can open the development server and check if everything is working ok.

$ python manage.py runserver

Now let’s move on to the users app.

Installing Necessary Libraries

We need two external libraries for our project. Pillow to handle images and xhtml2pdf to create PDF files. We can install both using pip.

$ pip install pillow
$ pip install xhtml2pdf

You can check out the installation guide on their official documentation:

Pillow installation

xhtml2pdf installation

Creating the User model

We will now create a model inside the users app. Let’s put the following code in models.py of the users app.

Now we need to make migrations and apply the migration.

$ python manage.py makemigrations
$ python manage.py migrate

A table named User is now created in the database and has three fields: name, logo, and description.

Populating the Database With Dummy Data

Now let’s log in to the admin panel and add some dummy data to the User model. I’ve added five fake users manually to the database so that we can work with them.

Dummy users to work with

Each of the user has a name, a logo, and a description. We will create PDF files with these data.

Each user has three fields

Creating Views

We need two views for the project. A UserListView where we will list the name of the users. With every name, we will create a button. By clicking this button we will create a PDF with the information of a user. To create PDF files we will need the second view users_render_pdf_view.

In users_render_pdf_view I copied most of the code from the documentation of xhtml2pdf. We need to make some minor changes to generate dynamic data from our User model. Here you can see the code on the documentation to compare where I’ve made the changes:

Using xhtml2pdf in Django

And UserListView is just a class-based view to list out all the users. Now we need to create our templates associated with each view.

Creating Templates

We need three HTML files:

  1. base.html (We will use template inheritance. The other two HTML files will inherit from base.html)
  2. main.html (This will act as the home page. Here we will show the names of the users and for each user, we will create a button to generate PDFs)
  3. generate_pdf.html (Contents of this page will be generated as PDF files. Here we will dynamically generate information of the users)

Inside the users app let’s create a folder named templates. Then inside the template folder let’s create one more folder named users. Here we will put our HTML files.

└── users
├── templates
│ └── users
│ ├── base.html
│ ├── generate_pdf.html
│ └── main.html

base.html

main.html

generate_pdf.html

Now we need to create separate URLs for each view.

Creating URLs

For this, we need to modify our project urls.py file. We can also create a separate urls.py file for our app and then include it in the project. However, to keep things simple I’ve created the URLs directly in the project urls.py.

And that’s pretty much it. Let’s see how the application looks like.

User list

This is the home page of our application. All the users are displayed here. Alongside the user’s name we have a button that says “Download pdf”. If we click the button we will be redirected to a new window where a PDF file with the information of a user will be created. Suppose we clicked the button beside User 3. The information of User 3 will appear in PDF format by clicking it.

Generated PDF

Of course, by clicking on some other user the information will change accordingly. We can also download the PDF and store it locally.

Conclusion

In this article, I showed you how to create PDFs from a Django website by creating a small project. I hope you find it helpful. Thanks for your time.

The source code is available on GitHub. I have also provided all the images used in this project. Here is the GitHub repository of the project. You can use the code from there.

--

--