Series: Python Comeback Journey — ← GitHub Authentication Hell | Next → Coming Soon
Template Inheritance in Flask: From HTML Soup to Professional Structure
How I learned to stop repeating code and love the base.html template.
The Problem with Repetition
When I started building my Flask app, each page had its own HTML returned directly from Python. It worked — but every change became a chore. If I wanted to add a navigation bar or a footer, I had to copy and paste the same code into multiple routes.
It was messy, error-prone, and broke the DRY (Don’t Repeat Yourself) rule. I knew there had to be a better way.
That’s when I discovered Jinja2, Flask’s built-in templating engine.
The Foundation: Creating a Base Template
The secret to maintainable Flask templates is inheritance. You start with a master template, base.html, which holds the site-wide structure — your HTML boilerplate, CSS links, navigation, and footer.
Here’s my base.html:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class=“xml”><span class=“hljs-meta”><!DOCTYPE html></span> <span class=“hljs-tag”><<span class=“hljs-name”>html</span> <span class=“hljs-attr”>lang</span>=<span class=“hljs-string”>“en”</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>head</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>meta</span> <span class=“hljs-attr”>charset</span>=<span class=“hljs-string”>“UTF-8”</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>meta</span> <span class=“hljs-attr”>name</span>=<span class=“hljs-string”>“viewport”</span> <span class=“hljs-attr”>content</span>=<span class=“hljs-string”>“width=device-width, initial-scale=1.0”</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>title</span>></span></span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>block</span></span> title %}</span><span class=“xml”>Book Tracker</span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>endblock</span></span> %}</span><span class=“xml”><span class=“hljs-tag”></<span class=“hljs-name”>title</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>link</span> <span class=“hljs-attr”>rel</span>=<span class=“hljs-string”>“stylesheet”</span> <span class=“hljs-attr”>href</span>=<span class=“hljs-string”>“</span></span></span><span class=”hljs–template–variable“>{{ url_for(‘static’, filename=’style.css’) }}</span><span class=”xml“><span class=”hljs–tag“><span class=”hljs–string“>”</span>></span> <span class=“hljs-tag”></<span class=“hljs-name”>head</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>body</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>nav</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>a</span> <span class=“hljs-attr”>href</span>=<span class=“hljs-string”>“</span></span></span><span class=”hljs–template–variable“>{{ url_for(‘home’) }}</span><span class=”xml“><span class=”hljs–tag“><span class=”hljs–string“>”</span>></span>Home<span class=“hljs-tag”></<span class=“hljs-name”>a</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>a</span> <span class=“hljs-attr”>href</span>=<span class=“hljs-string”>“</span></span></span><span class=”hljs–template–variable“>{{ url_for(‘contact’) }}</span><span class=”xml“><span class=”hljs–tag“><span class=”hljs–string“>”</span>></span>Contact<span class=“hljs-tag”></<span class=“hljs-name”>a</span>></span> <span class=“hljs-tag”></<span class=“hljs-name”>nav</span>></span> </span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>block</span></span> content %}</span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>endblock</span></span> %}</span><span class=“xml”> <span class=“hljs-tag”><<span class=“hljs-name”>footer</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>p</span>></span>&copy; 2024 Book Tracker — Built with Flask<span class=“hljs-tag”></<span class=“hljs-name”>p</span>></span> <span class=“hljs-tag”></<span class=“hljs-name”>footer</span>></span> <span class=“hljs-tag”></<span class=“hljs-name”>body</span>></span> <span class=“hljs-tag”></<span class=“hljs-name”>html</span>></span></span> |
This template defines placeholders using {% block %} tags. They mark areas that child templates can fill in.
Building on the Foundation: Child Templates
Once the base was ready, I created my first child template — home.html. Instead of rewriting the whole page, it only needed to provide content for the defined blocks.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>extends</span></span> “base.html” %}</span> <span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>block</span></span> title %}</span><span class=“xml”>Home — Book Tracker</span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>endblock</span></span> %}</span> <span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>block</span></span> content %}</span><span class=“xml”> <span class=“hljs-tag”><<span class=“hljs-name”>h1</span>></span>Welcome to Book Tracker<span class=“hljs-tag”></<span class=“hljs-name”>h1</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>p</span>></span>Start tracking your reading journey!<span class=“hljs-tag”></<span class=“hljs-name”>p</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>h2</span>></span>My Books<span class=“hljs-tag”></<span class=“hljs-name”>h2</span>></span> <span class=“hljs-tag”><<span class=“hljs-name”>ul</span>></span> </span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>for</span></span> book <span class=“hljs-keyword”>in</span> books %}</span><span class=“xml”> <span class=“hljs-tag”><<span class=“hljs-name”>li</span>></span><span class=“hljs-tag”><<span class=“hljs-name”>strong</span>></span></span><span class=“hljs-template-variable”>{{ book.title }}</span><span class=“xml”><span class=“hljs-tag”></<span class=“hljs-name”>strong</span>></span> by </span><span class=“hljs-template-variable”>{{ book.author }}</span><span class=“xml”><span class=“hljs-tag”></<span class=“hljs-name”>li</span>></span> </span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>endfor</span></span> %}</span><span class=“xml”> <span class=“hljs-tag”></<span class=“hljs-name”>ul</span>></span> </span><span class=“hljs-template-tag”>{% <span class=“hljs-name”><span class=“hljs-name”>endblock</span></span> %}</span> |
The {% extends "base.html" %} line tells Flask to use base.html as the layout. The {% block content %} section then gets inserted into the corresponding area of the base template.
Passing Data from Flask to Templates
Once I switched to templates, my Python code became cleaner and easier to manage. Instead of hardcoding HTML, I used Flask’s render_template() function to inject data into the page.
|
1 2 3 4 5 6 7 8 9 |
<span class=“hljs-keyword”>from</span> flask <span class=“hljs-keyword”>import</span> Flask, render_template app = Flask(__name__) <span class=“hljs-meta”>@app.route(‘/’)</span> <span class=“hljs-function”><span class=“hljs-keyword”>def</span> <span class=“hljs-title”>home</span><span class=“hljs-params”>()</span>:</span> books = get_all_books() <span class=“hljs-comment”># Function that fetches book data</span> <span class=“hljs-keyword”>return</span> render_template(<span class=“hljs-string”>‘home.html’</span>, books=books) |
The variable books is now available directly in the HTML template, making it easy to loop through and display dynamic data.
Tip: Jinja2 automatically escapes data by default, which helps prevent injection attacks — a huge win for both security and simplicity.
Why This Changed Everything
After I implemented template inheritance, my app finally felt professional. Everything became easier:
- No More Duplication: I could change my navigation in one file and see it updated across all pages.
- Cleaner Separation: Python handled logic; HTML handled presentation.
- Scalability: Adding new pages became trivial — just create a new child template and extend the base.
Moving from inline HTML to Jinja2 templates was the moment my small Flask script started to feel like a real web application.
This article is part of my Python Comeback Journey — documenting my return to coding after an 8-year gap.
← Previous: GitHub Authentication Hell





Leave a Reply