Django Tutorial on PDF Generation and Rendering with xhtml2pdf Package
Generating PDF is a very common task in web development. They are used for a variety of purposes such are reporting, invoice generation and accounting purposes, etc.
In Django, there is this package xhtml2pdf through which we can generate pdf. In this post you’ll be covering three main topics those are generating PDF from HTML and saving as PDF file in the directory, generating PDF from a template by passing context data and saving as PDF file in a directory, rendering PDF from a template in the browser.
Installation and Configuration of xhtml2pdf Package
Enter below command in terminal.
pip install xhtml2pdf
If you want to install the latest version for Python 3 then go with below command
pip install --pre xhtml2pdf
Setup App
First, let us create a project in Django to do so type below command in terminal.
django-admin startproject pdf_demo
Django will create you in the current directory you are in. Now move to the pdf_demo project root location and to create an app using below command.
django-admin startapp pdf
This will create an app named pdf now we must specify and let Django know the app exists by going to `settings.py` file and add app path below `INSTALLED_APPS`.
INSTALLED_APPS = [ '...', '...', 'pdf.apps.PdfConfig', ] STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, ""), os.path.join(BASE_DIR, "static") ] MEDIA_URL = 'media/' MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL)
In app pdf create urls.py file and go project directory’s urls.py file and include pdf.urls path.
#project/app/urls.py from django.urls import path from pdf import views urlpatterns = [ path('generate-pdf', views.generate_pdf, name="generate_pdf"), path('generate-pdf-through-template', views.generate_pdf_through_template, name="generate_pdf_through_template"), path('render-pdf', views.render_pdf, name="render_pdf"), ]
In project/project/urls.py.
from django.urls import path, include urlpatterns = [ path('pdf/', include('pdf.urls'), name='pdf'), ]
We have specified URLs its time to create views and templates for respective URLs.
Creating Views
First, we’ll create a function generate_pdf()
that basically saves a string file to a location. The string is nothing but HTML.
from django.shortcuts import render from django.http import HttpResponse from django.template.loader import render_to_string from io import BytesIO from xhtml2pdf import pisa from pdf.models import State def generate_pdf(request): html = '<html><body><p>To PDF or not to PDF</p></body></html>' write_to_file = open('media/test.pdf', "w+b") result = pisa.CreatePDF(html,dest=write_to_file) write_to_file.close() return HttpResponse(result.err)
Inside function generate_pdf(request)
the variable html
has HTML contents and we are writing files to directory media where it will be saved.
But most of the cases it is not always possible to create a variable like HTML in views to solve this issue in the next function we’ll make use of render_to_string
a template loader. We specify HTML in the templates section and call the template whenever there is a need of generating pdf through them.
def generate_pdf_through_template(request): context={} html = render_to_string('pdf/pdf_template.html',context) write_to_file = open('media/test_1.pdf', "w+b") result = pisa.CreatePDF(html,dest=write_to_file) write_to_file.close() return HttpResponse(result.err)
The above function saves the pdf as a file in the directory. There will be cases were insisted of saving PDF you need to render it in the browser. This is covered in the below code.
def render_pdf(request): path = "pdf/pdf_template.html" context = {"states" : State.objects.all()[:100]} html = render_to_string('pdf/pdf_template.html',context) io_bytes = BytesIO() pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), io_bytes) if not pdf.err: return HttpResponse(io_bytes.getvalue(), content_type='application/pdf') else: return HttpResponse("Error while rendering PDF", status=400)
We are passing information to the template through context
dictionary and it has states that have a key which has objects of states.
The BytesIO()
stores HTML data in memory and is efficient other than using open()
method.
While returning response we’ll specify it has application/pdf.
Creating Models
In pdf/models.py.
from django.db import models class State(models.Model): state_id = models.AutoField(primary_key=True) state_name = models.CharField(max_length=30, blank=True, null=True) country = models.ForeignKey(Country,related_name="country",on_delete=models.CASCADE,null=True) is_deleted = models.IntegerField(default=0) class Meta: managed = True db_table = 'states'
Creating Templates
In the app, pdf create HTML file pdf/templates/pdf/pdf_template.html.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Django PDF</title> <style> th, td{ padding: 5px 15px; } th{ font-size: 20px; font-weight: bold; } td{ font-size: 15px; } </style> </head> <body> <h1 style="text-align: center;" >List of States by respective Countries</h1> <table border="1" style="border-collapse: collapse;" > <thead> <tr> <td>Country Name</td> <td>Country Short Code</td> <td>State Name</td> </tr> </thead> <tbody> {% for value in states %} <tr> <td>{{value.country.country_name}}</td> <td>{{value.country.country_short}}</td> <td>{{value.state_name}}</td> </tr> {% endfor %} </tbody> </table> </body> </html>

Result of Django xhtml2pdf Package convert HTML to PDF Output
To learn more on converting HTML to PDF in Django visit xhtml2pdf package documentation.
Conclusion
You have reached the end of our post on Django Tutorial on PDF Generation and Rendering with xhtml2pdf Package. If you find anything difficult with our way of explanation please leave us a comment and if you have enjoyed reading our post then help us grow by share this post link.
Related Posts
- Django Forms | Custom Form Validations
- Python Django Forms | Creating, Rendering, Validating and Saving Forms




