Template inheritance is one of Django’s most powerful features for building dynamic, maintainable, and DRY (Don’t Repeat Yourself) web applications. It allows developers to define base templates with common structures and extend them in child templates, while selectively overriding sections using {% block %}
. This guide provides a detailed step-by-step explanation of template inheritance, reusability, best practices, and practical examples to help you master this core Django feature.
1. Introduction to Django Templates
Django templates separate presentation logic from business logic. Templates are HTML files with special Django template tags and variables to render dynamic content. The template system ensures:
- Reusability of common layouts.
- Clear separation between Python code and HTML.
- Flexibility to inject dynamic content.
A basic template may look like this:
<!DOCTYPE html>
<html>
<head>
<title>{{ page_title }}</title>
</head>
<body>
<h1>{{ heading }}</h1>
<p>{{ content }}</p>
</body>
</html>
In views, you render the template:
from django.shortcuts import render
def home(request):
context = {
'page_title': 'Home',
'heading': 'Welcome to My Site',
'content': 'This is the home page.'
}
return render(request, 'home.html', context)
2. The Problem of Repetition
Without template inheritance, every HTML page requires repeating the same header, navigation, footer, and CSS links. For example:
<!-- page1.html -->
<!DOCTYPE html>
<html>
<head>
<title>Page 1</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav>Menu</nav>
<h1>Page 1</h1>
<footer>Footer content</footer>
</body>
</html>
<!-- page2.html -->
<!DOCTYPE html>
<html>
<head>
<title>Page 2</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav>Menu</nav>
<h1>Page 2</h1>
<footer>Footer content</footer>
</body>
</html>
This is inefficient and hard to maintain. Template inheritance solves this problem.
3. Base Templates
A base template defines the common structure for your site. Typically, it includes:
<head>
with meta tags, CSS links, and JavaScript.- Header and navigation menus.
- Footer.
- Placeholder
{% block %}
tags for dynamic content.
Example: base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Site{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="{% url 'home' %}">Home</a></li>
<li><a href="{% url 'about' %}">About</a></li>
<li><a href="{% url 'contact' %}">Contact</a></li>
</ul>
</nav>
</header>
<main>
{% block content %}
<!-- Child templates will override this -->
{% endblock %}
</main>
<footer>
<p>&copy; 2025 My Site</p>
</footer>
{% block scripts %}
<!-- Child templates can add scripts here -->
{% endblock %}
</body>
</html>
{% block title %}
allows child templates to define page-specific titles.{% block content %}
is the main placeholder for page content.{% block scripts %}
allows additional JavaScript to be added in child templates.
4. Extending Base Templates
Child templates use {% extends 'base.html' %}
to inherit the base template. They override blocks as needed.
Example: home.html
{% extends 'base.html' %}
{% block title %}Home Page{% endblock %}
{% block content %}
<h1>Welcome to My Website</h1>
<p>This is the homepage content.</p>
{% endblock %}
{% block scripts %}
<script>
console.log('Home page loaded');
</script>
{% endblock %}
home.html
inherits all structure frombase.html
.- Only the
title
,content
, andscripts
blocks are overridden. - Header, navigation, footer, and CSS remain consistent across pages.
Example: about.html
{% extends 'base.html' %}
{% block title %}About Us{% endblock %}
{% block content %}
<h1>About Our Company</h1>
<p>We are committed to delivering quality products.</p>
{% endblock %}
- No need to rewrite navigation or footer.
- Blocks make it easy to inject page-specific content.
5. Block Nesting
Blocks can be nested inside each other in base templates. Child templates can override outer or inner blocks.
Example:
<!-- base.html -->
<body>
<header>
{% block header %}
<h1>Site Header</h1>
{% endblock %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}
<p>Default Footer</p>
{% endblock %}
</footer>
</body>
Child template can override only a specific block:
{% extends 'base.html' %}
{% block content %}
<p>Page-specific content here.</p>
{% endblock %}
Header and footer remain unchanged.
6. Using {% include %}
for Reusability
In addition to {% extends %}
, you can include reusable partial templates using {% include %}
:
Example: _navbar.html
<nav>
<ul>
<li><a href="{% url 'home' %}">Home</a></li>
<li><a href="{% url 'about' %}">About</a></li>
<li><a href="{% url 'contact' %}">Contact</a></li>
</ul>
</nav>
Include it in the base template:
<header>
{% include 'partials/_navbar.html' %}
</header>
_navbar.html
can be reused in multiple base templates.- Keeps large templates organized and easier to maintain.
7. Template Directory Structure
A recommended structure for Django templates:
myapp/
templates/
base.html
home.html
about.html
contact.html
partials/
_navbar.html
_footer.html
- Place reusable partials in
partials/
. - Base templates at the root of the template folder.
- Child templates in the same folder or organized in subfolders.
8. Passing Context to Templates
Templates render dynamic content via context dictionaries provided in views:
def home(request):
context = {
'page_title': 'Home Page',
'welcome_message': 'Welcome to My Site'
}
return render(request, 'home.html', context)
In home.html
:
{% block content %}
<h1>{{ welcome_message }}</h1>
{% endblock %}
- Context variables can be used in any block of the template.
- Variables propagate through inheritance.
9. Overriding Base Blocks Multiple Levels
Template inheritance can have multiple levels:
base.html -> layout.html -> page.html
layout.html
extendsbase.html
and overrides or adds new blocks.page.html
extendslayout.html
.- This allows creating themeable and modular templates.
Example:
<!-- layout.html -->
{% extends 'base.html' %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
<!-- page.html -->
{% extends 'layout.html' %}
{% block page_content %}
<p>Page-specific content goes here.</p>
{% endblock %}
page.html
does not touchbase.html
directly, promoting modularity.
10. Using {{ block.super }}
If you want to append content to a block instead of replacing it completely, use {{ block.super }}
:
<!-- base.html -->
{% block content %}
<p>Base content here.</p>
{% endblock %}
<!-- child.html -->
{% extends 'base.html' %}
{% block content %}
{{ block.super }}
<p>Child-specific content.</p>
{% endblock %}
- Output includes both base content and child content.
- Useful for adding scripts, messages, or notifications.
11. Static Files in Templates
Combine template inheritance with static files:
<!-- base.html -->
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<script src="{% static 'js/main.js' %}"></script>
Child templates can add page-specific styles or scripts:
{% block scripts %}
<script src="{% static 'js/home.js' %}"></script>
{% endblock %}
- Ensures common assets are loaded in base template.
- Reduces redundancy and simplifies asset management.
12. Template Filters and Tags
Django templates provide filters ({{ variable|filter }}
) and tags ({% tag %}
) that work with inheritance:
- Filters:
|upper
,|lower
,|date
,|truncatechars
- Tags:
{% if %}
,{% for %}
,{% url %}
,{% csrf_token %}
Example with inheritance:
{% block content %}
<h1>{{ welcome_message|upper }}</h1>
<ul>
{% for post in posts %}
<li>{{ post.title|truncatechars:50 }}</li>
{% endfor %}
</ul>
{% endblock %}
- Child template can dynamically render content while reusing base layout.
13. Reusable Forms in Templates
Forms are commonly reused in multiple pages. Place them in partial templates:
<!-- _contact_form.html -->
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
Include in contact.html
:
{% extends 'base.html' %}
{% block content %}
<h1>Contact Us</h1>
{% include 'partials/_contact_form.html' %}
{% endblock %}
- Enables reusing the same form structure in multiple pages.
- Simplifies maintenance and consistent styling.
14. Advanced Inheritance Patterns
- Theme-Based Templates: Create multiple base templates for different themes (
base_light.html
,base_dark.html
). - Conditional Blocks: Use
{% if %}
inside blocks for dynamic content. - Template Libraries: Create custom template tags and filters for repeated logic.
- AJAX Content: Use blocks to load dynamic sections via JavaScript.
15. Best Practices
- Keep Base Templates Minimal: Only include common structure.
- Use Descriptive Block Names: Avoid generic names like
block1
. - Organize Templates Logically: Group partials and child templates in folders.
- Avoid Inline CSS/JS: Load scripts and styles in blocks.
- Use
{% include %}
for Small Reusable Parts: Navigation bars, footers, forms. - Use
{% block.super %}
Wisely: Append content without duplicating. - Document Blocks: For large teams, document purpose of each block.
16. Real-World Example: Blog Application
Base template (base.html
):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Blog{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
{% include 'partials/_navbar.html' %}
<main>
{% block content %}{% endblock %}
</main>
{% include 'partials/_footer.html' %}
{% block scripts %}{% endblock %}
</body>
</html>
Blog list page (post_list.html
):
{% extends 'base.html' %}
{% block title %}Blog Posts{% endblock %}
{% block content %}
<h1>All Blog Posts</h1>
<ul>
{% for post in posts %}
<li><a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a></li>
{% empty %}
<li>No posts available.</li>
{% endfor %}
</ul>
{% endblock %}
Blog detail page (post_detail.html
):
{% extends 'base.html' %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p>Published on {{ post.published_date|date:"F d, Y" }}</p>
{% endblock %}
- Base template ensures consistent layout, navigation, and footer.
- Child templates override only necessary blocks.
- Adding new pages requires minimal effort, improving maintainability.
Leave a Reply