このページでは、Djangoのテンプレートシステムにおける1つの核心的な概念「テンプレートの継承」について詳しく説明します。
テンプレートの継承とは、一言で言うと、既存のテンプレート(通常はベースとなるテンプレート)から特定の部分を再利用し、その上で新たな要素を追加したり、既存の要素をオーバーライド(上書き)したりすることを可能にする機能です。


このページでは、テンプレートの継承とは何か?そのメリットと使用方法、さらには実践的な例とベストプラクティスを含む具体的な応用例を紹介します。
Djangoを用いたWeb開発を行いたい人であれば、知らないと恥ずかしい超・基本知識の1つです。是非最後までご覧ください。
Django:テンプレートの継承とは
テンプレートの継承とは、あるテンプレート(一般的にはベーステンプレートと呼ばれます)が持つ一部または全部の構造や要素を再利用し、新たなテンプレートを作る方法を指します。

例えば、画面のヘッダーやフッターを定義するベーステンプレートを用意しておいて、そのテンプレートをもとに、ヘッダーやフッターだけを編集した別のテンプレートを作るようなイメージ。

これは、DjangoがDRY(Don't Repeat Yourself)というコーディング原則を反映したもので、同じコードを何度も書く必要を減らし、保守性と効率性を向上させるための機能の1つ。
テンプレートの継承を理解するために、以下の2点について学習していきます。
- 基底テンプレートの作成: Djangoのテンプレートシステムでは、ベースとなるテンプレートを作成し、その中に再利用したいHTMLの構造や要素を定義。ナビゲーションバー、フッター、スタイルシートのリンク、スクリプトのリンクなど、複数のページで共有される要素を含みます。
- ブロックの定義とオーバーライド: ベーステンプレートには、子テンプレートでオーバーライド(上書き)または追加できるブロックを定義します。これらのブロックは、ページタイトル、メインコンテンツ、追加のスタイルシートなど、ページによって異なる可能性のある部分を占めます。

上記2点についてステップバイステップで解説していきます。
ベーステンプレートの作成
まず、ベース(親)テンプレートを作成します。このベーステンプレートには、すべてのテンプレートから共有される「共通のHTML構造」を定義します。
参考 HTMLとは?

以下がベーステンプレート(base.html)の例です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{% block title %}デフォルトタイトル{% endblock title %}</title>
<!-- その他のメタ情報、スタイルシートのリンクなど -->
</head>
<body>
<header>
<!-- ヘッダーの内容 -->
</header>
<main>
{% block content %}
<!-- この部分は子テンプレートでオーバーライドされます -->
{% endblock content %}
</main>
<footer>
<!-- フッターの内容 -->
</footer>
<!-- その他のスクリプトなど -->
</body>
</html>
参考 HTMLの基本構造・書き方 / <header>タグ /<main>タグ
ここでは{% block title %}と{% block content %}の2つのブロックを定義しています。これらは子テンプレートでオーバーライドすることができます。

ここで、blockタグについて詳細を補足します。
Django:blockタグ
block はテンプレート内で再利用可能なセクションを定義するためのタグです。ベーステンプレートでは block を使用して子テンプレートで上書き(オーバーライド)できるセクションを定義します。
{% block blockname %}
Default content...
{% endblock %}
blockname はブロックの名前(任意の名前)で、Default content... はブロックのデフォルトコンテンツ(子テンプレートでオーバーライドされない場合に表示される部分)です。
このblockタグを利用した部分は、次に解説する子テンプレートで上書きをすることが可能になるということです。
子テンプレートの作成とベーステンプレートからの継承
次に、ベーステンプレートから継承する子テンプレートを作成します。
子テンプレートでは、{% extends "base.html" %}を使用して基底テンプレートから継承を行い、{% block %}タグを使用して基底テンプレートのブロックをオーバーライドします。
{% extends "base.html" %}
{% block title %}マイページ{% endblock title %}
{% block content %}
<h1>ようこそ、マイページへ!</h1>
<!-- その他のコンテンツ -->
{% endblock content %}
参考 hタグ
この子テンプレートでは、基底テンプレートのtitleブロックとcontentブロックをオーバーライドしています。

分かりやすく言うと、1行目のextendsタグで「base.html」をテンプレートにするよ!と宣言。
そして、blockタグで「この部分はこの内容で上書きするよ!」という処理を書いています。
具体的なサンプルコードを見てみましょう。
例えば、以下のようなベーステンプレートがあるとします。
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}デフォルトタイトル{% endblock %}</title>
</head>
<body>
<header>
<nav>
<!-- ナビゲーションメニュー -->
</nav>
</header>
<main>
{% block content %}
<!-- デフォルトコンテンツ:子テンプレートでオーバーライド予定 -->
{% endblock %}
</main>
<footer>
<!-- フッター情報 -->
</footer>
</body>
</html>
参考 navタグ
このテンプレートをもとに、{% block %}タグを使ってベーステンプレートのブロックをオーバーライド↓。
{% extends "base.html" %}
{% block title %}
マイページ
{% endblock %}
{% block content %}
<h1>ようこそ、マイページへ!</h1>
<p>ここは、ユーザーのマイページです。</p>
{% endblock %}
参考 pタグ(段落タグ)

こうすることで、ベーステンプレートをもとにして、必要な部分だけを上書きした画面が出来上がるというわけです。

ここでは、ブログアプリを開発する場合の例を用いて、具体的にテンプレートの継承がどのように活用されるか?をご紹介します。
ステップ1 基底テンプレートの作成
ブログの全てのページで共有される要素(例えばナビゲーションバー、フッター、ヘッダー画像等)を含む基底テンプレート(base.html)を作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Blog{% endblock title %}</title>
<!-- その他のメタ情報、スタイルシートのリンクなど -->
</head>
<body>
<header>
<!-- ヘッダーの内容 -->
</header>
<nav>
<!-- ナビゲーションバーの内容 -->
</nav>
<main>
{% block content %}
<!-- この部分は子テンプレートでオーバーライドされます -->
{% endblock content %}
</main>
<footer>
<!-- フッターの内容 -->
</footer>
<!-- その他のスクリプトなど -->
</body>
</html>
ステップ2 ブログ投稿ページのテンプレート作成
次に、基底テンプレートから継承したブログ投稿ページのテンプレート(post_detail.html)を作成します。このテンプレートでは、基底テンプレートのcontentブロックをオーバーライドします。
{% extends "base.html" %}
{% block title %}
{{ post.title }} - My Blog
{% endblock title %}
{% block content %}
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
{% endblock content %}
上記のようにして、すべてのページで共有される基本的なHTML構造を維持しつつ、個々のページで異なる内容を表示できます。これにより、コードの重複を避け、保守性と可読性を向上させることができます。
継承するHTMLファイルの指定
Djangoではextendsタグを使って他のテンプレートを継承する際、テンプレートファイルの位置はTEMPLATES設定のDIRSオプションに指定されたディレクトリが検索対象となります。

つまり、extendsで指定するテンプレートファイルは同じフォルダに存在する必要はありません。
通常、Djangoプロジェクトはプロジェクト全体のテンプレートを配置するためのtemplatesディレクトリを持ち、各アプリケーションディレクトリの下にも各々templatesディレクトリを持つ構造になっています。
myproject/
├── manage.py
├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
├── templates/
│ ├── base.html
└── todo/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
├── models.py
├── templates/
│ ├── todo/
│ │ ├── index.html
│ │ ├── task_form.html
│ │ └── delete_confirm.html
├── tests.py
├── urls.py
└── views.py
ここで、各アプリケーションディレクトリ(↑の例言えば「todo」)の下にあるtemplatesディレクトリには、そのアプリケーション専用のテンプレートを配置。
プロジェクト全体で共有するテンプレート(↑の例で言えば「base.html」)はプロジェクトレベルのtemplatesディレクトリに配置します。

settings.pyのTEMPLATES設定ではDIRSオプションにプロジェクトレベルのtemplatesディレクトリのパスを含めます。
# settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # BASE_DIRはプロジェクトのルートディレクトリを表す
...
},
]
このようにすることで、base.htmlのようなプロジェクト全体で共有するテンプレートを各アプリケーションから参照することが可能になります。
したがって、todoアプリケーションのindex.htmlからbase.htmlを継承するには、以下のように記述すればOKということ。
{% extends 'base.html' %}
...

extendsタグで指定するテンプレートファイルの位置は、TEMPLATES設定のDIRSオプションで指定されたディレクトリの下であればどこでもかまいません。
Django:extends/blockのまとめ

初めてPythonを勉強するのは結構難しいですよね。
でもその悩みを抱えているのは一人じゃありません。全てのPython使いが同じ道を進んできました。
Pythonをはじめとするプログラミングスキルを武器に、時間と場所に捉われない自由な生き方を目指してみませんか?今すぐ行動したい方は以下の記事をチェック!
読者料典 Python入門:学習カリキュラム ←こちらから!
