Template Inheritance & Includes
The task list template from the last lesson only showed a <ul> of tasks — a real
TaskFlow page also needs a navigation bar, a page title, maybe a footer, and that same scaffolding
has to appear identically on every single page in the app. Copy-pasting that HTML into every
template is exactly the kind of repetition Django templates are built to avoid, through
inheritance and includes.
The Copy-Paste Problem Inheritance Solves
Without inheritance, updating TaskFlow's navigation bar would mean editing the same HTML in
task_list.html, task_detail.html, project_list.html, and
every other template — and forgetting one leaves that page looking subtly broken. Template
inheritance flips the relationship: one template defines the shared page skeleton once,
and every other template fills in only the part that's actually different.
Building a Base Template with a Block
A base template is ordinary HTML with one or more {% block %} regions marking the
spots a child template is allowed to override. Anything outside a block is fixed and appears on
every page unchanged.
{# taskflow/templates/base.html #}
<!DOCTYPE html>
<html>
<head>
<title>TaskFlow</title>
</head>
<body>
<nav>
<a href="/">TaskFlow</a>
<a href="/tasks/">Tasks</a>
<a href="/projects/">Projects</a>
</nav>
<main>
{% block content %}
{% endblock %}
</main>
<footer>© TaskFlow</footer>
</body>
</html>
The {% block content %}{% endblock %} pair marks a named, overridable region called
content. Left empty here, it's just a placeholder waiting for a child template to
supply something.
Extending the Base Template in a Child
A child template starts with {% extends %} naming the base template, then defines a
{% block %} with a matching name — only the content inside that block replaces the
base's version. Everything else in base.html (the nav, the footer, the surrounding
HTML) is inherited automatically.
{# taskflow/templates/taskflow/task_list.html #}
{% extends 'base.html' %}
{% block content %}
<h1>Your Tasks</h1>
<ul>
{% for task in tasks %}
<li>{{ task.title }}</li>
{% empty %}
<li>No tasks yet.</li>
{% endfor %}
</ul>
{% endblock %}
{% extends %} must be the very first tag in the file — nothing, not even a comment,
can come before it. Anything in task_list.html outside of a {% block %}
is simply ignored, since the base template controls the overall page structure.
Multiple Named Blocks in One Base Template
A base template isn't limited to one block. Adding a second block for the page's
<title> lets each child control the browser tab title without touching anything
else:
{# taskflow/templates/base.html #}
<head>
<title>{% block title %}TaskFlow{% endblock %}</title>
</head>
<body>
<main>
{% block content %}
{% endblock %}
</main>
</body>
{# taskflow/templates/taskflow/task_list.html #}
{% extends 'base.html' %}
{% block title %}Your Tasks - TaskFlow{% endblock %}
{% block content %}
<h1>Your Tasks</h1>
{# ... task list markup ... #}
{% endblock %}
Note that {% block title %}TaskFlow{% endblock %} in the base template supplies a
default — a child template that doesn't override title at all still gets
"TaskFlow" as the page title, since an un-overridden block just renders its own content.
Reusable Fragments with {% include %}
Inheritance solves whole-page structure, but a single task also shows up as a small, self-contained
card in more than one place — the task list page and, later, a dashboard page. Rebuilding that
markup with {% extends %} doesn't make sense, since a task card isn't a full page.
{% include %} pulls in a smaller template fragment right where it's referenced:
{# taskflow/templates/partials/task_card.html #}
<div class="task-card">
<strong>{{ task.title }}</strong>
<p>{{ task.description|truncatewords:15 }}</p>
<span>{{ task.due_date|date:"M d, Y"|default:"No due date" }}</span>
</div>
{# taskflow/templates/taskflow/task_list.html #}
{% extends 'base.html' %}
{% block content %}
<h1>Your Tasks</h1>
{% for task in tasks %}
{% include 'partials/task_card.html' %}
{% endfor %}
{% endblock %}
The distinction to keep straight: extends is for whole-page inheritance — a child template fills in gaps in one parent page structure, and a template can only extend one base. include is for a reusable snippet — pulling one smaller template into another, as many times, and in as many different templates, as needed.
Passing Extra Context to an Include
Inside the loop above, {% include %} already has access to task, since
included templates share the surrounding template's context by default. When the variable name
doesn't line up — say, a dashboard page tracks a single highlighted task under the name
current_task instead of task — with maps it explicitly:
{# taskflow/templates/taskflow/dashboard.html #}
{% extends 'base.html' %}
{% block content %}
<h2>Today's Focus</h2>
{% include 'partials/task_card.html' with task=current_task %}
{% endblock %}
task_card.html still refers to task internally and needs no changes at
all — the with task=current_task clause is what bridges the naming gap between the two
templates.
|truncatewords and |date filters used
throughout this lesson all ship with Django. Custom
Template Tags & Filters shows how to add your own when TaskFlow needs a formatting rule Django
doesn't provide.