Write HTML in Python with Jinja2

Dec 31, 2021

When building this blog, I knew I wanted to stay away from the overhead of working with a database. That meant I'd need to generate all my pages as static HTML pages which could be returned from the server.

The simplest way to create these pages would be to write all the necessary HTML myself. I could create a sample page and duplicate it for each of the individual pages. However, if I ever wanted to make sweeping changes to all of the pages (like adding a new item to the page navigation), I would need to manually edit each file. That wasn't going to work.

I decided to turn to Jinja for rendering HTML templates. Jinja allows me to use template inheritance to write a base page and reuse it on each page. This allows me to store the page navigation, header, and footer in a single template. Then each individual page can extend this page and simply add the main content. Fortunately, I'm already familiar with the template syntax from Django!

To get started, I needed to add Jinja to my Python project.

pip install jinja2

Next, we need to set up our folder structure to house our Jinja HTML templates and our generated HTML files.

mkdir template  # for our Jinja templates
mkdir public # for our generated .html files

Now let's create a Jinja template HTML document.

touch template/sample_template.html

And add some basic HTML to it.

<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ title }}</title>
</head>
<body>
<div>This is the header</div>
<div>{{ content }}</div>
<div>This is the footer</div>
</body>
</html>

Notice that we're using Jinja template variables rather than writing page title and content manually. This will allow us to reuse this template for multiple pages.

Great! Now we're ready to start writing some Python code. We'll create a script which regenerates the HTML files each time it runs. That way, we don't need to worry about which files need to be generated.

touch generate.py

Now let's open up our new generate.py script.

import shutil
import os

from jinja2 import Environment
from jinja2 import FileSystemLoader


TEMPLATE_NAME = 'sample_template.html'
OUTPUT_FILE_NAME = 'sample_output.html'


class HtmlGenerator(object):
def __init__(self, template_name):
self.template_name = template_name
self.env = Environment(loader=FileSystemLoader('template'))

def _build_path(self, suffix):
# Build the full file path based on our current directory
current_directory = os.getcwd()
return os.path.join(current_directory, suffix)

def generate(self):
public_folder_path = self._build_path('public')
# If the public folder exists, then throw it away so we can regenerate it
if os.path.isdir(public_folder_path):
shutil.rmtree(public_folder_path)
os.mkdir(public_folder_path)


if __name__ == '__main__':
html_generator = HtmlGenerator(TEMPLATE_NAME)
html_generator.generate()

We've got all of our structure in place! Now all that is left to do is write the new HTML from the rendered template.

def generate(self):
public_folder_path = self._build_path('public')
# If the public folder exists, then throw it away so we can regenerate it
if os.path.isdir(public_folder_path):
shutil.rmtree(public_folder_path)
os.mkdir(public_folder_path)

# Get Jinja template
template = self.env.get_template(self.template_name)
with open(self._build_path('public/%s' % OUTPUT_FILE_NAME), 'w') as html_file:
html = template.render(
title="Sample Page",
content="Hello World!"
)
# Write the rendered template to the html file
html_file.write(html)

Now we can run our script in Python to generate the file.

python generate.py

If everything goes as expected, you should have a new file in the public directory called sample_output.html. Open it in your browser to view your new webpage!

$ tree
.
├── generate.py
├── public
│ └── sample_output.html
└── template
└── sample_template.html

Just think of all the possibilities! Now, you can write a small bit of Python to generate multiple HTML files while providing unique titles and content for each. The post you're reading was generated in the same way!

Make sure to stay tuned to see how to use Markdown in your new Jinja project!