Django テンプレートの継承で共通レイアウトをまとめる

Web アプリケーションでは、ヘッダー・フッター・ナビゲーションなどのレイアウトが全ページで共通になることがほとんどです。Django テンプレートの「継承」を使えば、共通部分を 1 か所にまとめ、ページごとに異なる部分だけを差し替えることができます。

ベーステンプレートを作る

まず、全ページの土台となるベーステンプレートを作ります。差し替え可能な領域を {% block %} タグで定義します。

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Site{% endblock %}</title>
    {% block extra_css %}{% endblock %}
</head>
<body>
    <header>
        <nav>
            <a href="/">ホーム</a>
            <a href="/articles/">記事一覧</a>
        </nav>
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        <p>&copy; 2025 My Site</p>
    </footer>

    {% block extra_js %}{% endblock %}
</body>
</html>

{% block content %}{% endblock %} のように名前を付けた領域が、子テンプレートで上書きできるポイントになります。ブロックの中にデフォルトの内容を書いておくこともでき、子テンプレートが上書きしなければそのまま表示されます。

子テンプレートで継承する

子テンプレートは {% extends %} でベーステンプレートを指定し、必要なブロックだけを上書きします。

<!-- templates/articles/list.html -->
{% extends "base.html" %}

{% block title %}記事一覧 | My Site{% endblock %}

{% block content %}
<h1>記事一覧</h1>
<ul>
    {% for article in articles %}
        <li>{{ article.title }}</li>
    {% endfor %}
</ul>
{% endblock %}

{% extends %} は必ずテンプレートの 1 行目に書かなければなりません。それより前にテキストや HTML があるとエラーになります。

ベーステンプレート(base.html)

共通レイアウトを定義し、block タグで差し替えポイントを用意する

子テンプレート

extends でベースを継承し、必要な block だけを上書きする

block.super で親の内容を活かす

子テンプレートでブロックを上書きすると、ベーステンプレートのデフォルト内容は完全に置き換わります。親の内容を残しつつ追記したい場合は {{ block.super }} を使います。

{% extends "base.html" %}

{% block extra_css %}
{{ block.super }}
<link rel="stylesheet" href="/static/articles/style.css">
{% endblock %}

ベーステンプレートの extra_css ブロックに何か書かれていれば、それを残したまま新しいスタイルシートを追加できます。共通の CSS を維持しながらページ固有の CSS を足すパターンで役立ちます。

多段階の継承

テンプレートの継承は多段階にできます。「サイト全体のベース → セクションごとのベース → 個別ページ」のような構成が典型的です。

<!-- templates/base.html -->
<!-- サイト全体の共通レイアウト -->

<!-- templates/articles/base.html -->
{% extends "base.html" %}
{% block content %}
<div class="article-layout">
    <aside>サイドバー</aside>
    <div>{% block article_content %}{% endblock %}</div>
</div>
{% endblock %}

<!-- templates/articles/detail.html -->
{% extends "articles/base.html" %}
{% block article_content %}
<h1>{{ article.title }}</h1>
<p>{{ article.body }}</p>
{% endblock %}

base.html(サイト全体の共通レイアウト)

articles/base.html(記事セクションのレイアウト)

articles/detail.html(個別ページの内容)

階層が深くなりすぎると追いかけにくくなるため、実務では 2〜3 段階に抑えるのが一般的です。

include で部品を切り出す

継承とは別に、テンプレートの一部を別ファイルに切り出して {% include %} で読み込む方法もあります。

<!-- templates/articles/list.html -->
{% extends "base.html" %}

{% block content %}
<h1>記事一覧</h1>
{% for article in articles %}
    {% include "articles/_card.html" %}
{% endfor %}
{% endblock %}
<!-- templates/articles/_card.html -->
<div class="card">
    <h2>{{ article.title }}</h2>
    <p>{{ article.body|truncatechars:80 }}</p>
</div>

include は継承関係を持たず、呼び出し元のコンテキスト変数をそのまま引き継ぎます。ファイル名の先頭に _ を付けるのは「単体では使わない部品テンプレートである」ことを示す慣習です。

テンプレートの継承で共通レイアウトを集約し、include で再利用可能な部品を切り出す——この 2 つの仕組みを使い分けることで、保守しやすいテンプレート設計が実現できます。