2.

Django Template の定義方法完全ガイド — 継承と部分挿入

編集
この記事の要点
  • Django Template は settings.pyTEMPLATES 設定 + 各アプリの templates/ ディレクトリで動く
  • 探索順: DIRS リスト → APP_DIRS=True ならインストール済アプリの templates/
  • 継承: 親に {% block content %}{% endblock %}、子で {% extends "base.html" %} + {% block content %}...{% endblock %}
  • 部分挿入: {% include "header.html" %} でファイルを差し込む
  • HTML エスケープはデフォルト ON{{ var|safe }} または {% autoescape off %} で解除(XSS 注意)
  • カスタムタグ/フィルタは templatetags/ 配下で定義し、{% load mytags %} で読み込む

settings.py — TEMPLATES 設定

# myproject/settings.py
import os
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],   # プロジェクト全体共通
        'APP_DIRS': True,                                # 各アプリの templates/ も探索
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

ディレクトリ配置

myproject/
├── manage.py
├── myproject/
│   └── settings.py
├── templates/                  ← プロジェクト共通(DIRS で指定)
│   ├── base.html
│   └── 404.html
└── myapp/
    ├── views.py
    └── templates/              ← アプリ別(APP_DIRS=True で自動)
        └── myapp/              ← ネームスペース推奨
            ├── article_list.html
            └── article_detail.html

アプリの templates/ 直下にファイルを置くと、他アプリの同名ファイルと衝突するため、templates/myapp/ のようにアプリ名のサブディレクトリを挟むのが Django 標準のベストプラクティスです。

ベーステンプレート (継承元)

{# templates/base.html #}



  
  {% block title %}MySite{% endblock %}
  {% block extra_head %}{% endblock %}


  {% include "header.html" %}

  
{% block content %}{% endblock %}
{% include "footer.html" %} {% block extra_js %}{% endblock %}

子テンプレート (継承)

{# templates/myapp/article_detail.html #}
{% extends "base.html" %}
{% load static %}

{% block title %}{{ article.title }} | MySite{% endblock %}

{% block extra_head %}
  
{% endblock %}

{% block content %}
  

{{ article.title }}

投稿者: {{ article.author.name }} / {{ article.created_at|date:"Y-m-d H:i" }}

{{ article.body|linebreaks }}

コメント ({{ comments|length }})

{% for c in comments %}
{{ c.author }}: {{ c.body }}
{% empty %}

コメントはまだありません。

{% endfor %}
{% endblock %}

部分挿入 (include)

{# templates/header.html #}
MySite
{# 呼び出し側 #} {% include "header.html" %} {# 変数を渡しながら include #} {% include "card.html" with title="ようこそ" body=user.name only %} {# only を付けると親コンテキストを渡さない #}

テンプレートタグ

タグ意味
{% if %} / {% elif %} / {% else %}条件分岐
{% for x in xs %} / {% empty %}ループ
{% url 'name' arg %}URL 逆引き
{% csrf_token %}CSRF トークン
{% block %} / {% extends %}継承
{% include %}部分挿入
{% load %}カスタムタグライブラリ読込
{% with %}一時変数
{% spaceless %}タグ間の空白除去
{% autoescape on/off %}HTML エスケープ切替

フィルタ (パイプ)

{{ name|upper }}                        {# 大文字 #}
{{ body|truncatechars:100 }}            {# 100 文字で省略 #}
{{ created_at|date:"Y-m-d H:i" }}       {# 日付フォーマット #}
{{ price|floatformat:2 }}               {# 小数2位 #}
{{ html|safe }}                          {# エスケープしない(XSS 注意) #}
{{ list|length }}                        {# 件数 #}
{{ value|default:"N/A" }}                {# 値が偽なら N/A #}
{{ text|linebreaks }}                    {# 改行を 


に #} {{ user.name|escape }} {# 明示的にエスケープ #}

autoescape — XSS 対策

Django テンプレートはデフォルトで全変数を HTML エスケープします。 をそのまま埋めても文字列として表示されます。

{# 通常: エスケープされる #}
{{ user_input }}
{# → <script>alert(1)</script> #}

{# 明示的にエスケープしない(XSS 注意) #}
{{ html_content|safe }}

{# ブロック単位で切替 #}
{% autoescape off %}
  {{ trusted_html }}
{% endautoescape %}

safeautoescape off はサニタイズ済みデータにのみ使うこと。ユーザー入力に直接使うと XSS の温床になります。

カスタムタグ・フィルタ

myapp/
└── templatetags/
    ├── __init__.py
    └── myapp_extras.py
# myapp/templatetags/myapp_extras.py
from django import template

register = template.Library()

@register.filter
def yen(value):
    """ 1000000 → 1,000,000 円 """
    try:
        return f"{int(value):,} 円"
    except (TypeError, ValueError):
        return value

@register.simple_tag
def current_year():
    import datetime
    return datetime.date.today().year

@register.inclusion_tag('myapp/_pagination.html')
def show_pagination(page_obj):
    return {'page_obj': page_obj}
{# テンプレート側で読み込み #}
{% load myapp_extras %}

{{ order.amount|yen }}                  {# → 1,000,000 円 #}
© {% current_year %} MySite
{% show_pagination page_obj %}

テンプレート探索順

  1. TEMPLATES.DIRS リスト(順序通り)
  2. APP_DIRS=True なら INSTALLED_APPS の順に各アプリの templates/

先に見つかったものが使われます。サードパーティアプリのテンプレートを上書きしたい場合は、DIRS に書いたプロジェクト共通 templates/ 配下に同じパスで配置すれば優先されます。

# 例: django.contrib.admin/templates/admin/index.html を上書きしたい
templates/
└── admin/
    └── index.html     ← これがあれば admin デフォルトを上書き

View からテンプレートを呼ぶ

# myapp/views.py
from django.shortcuts import render
from .models import Article

def article_detail(request, slug):
    article = Article.objects.get(slug=slug)
    return render(request, 'myapp/article_detail.html', {
        'article': article,
        'comments': article.comments.all(),
    })

# CBV の場合
from django.views.generic import DetailView
class ArticleDetailView(DetailView):
    model = Article
    template_name = 'myapp/article_detail.html'    # 省略時は myapp/article_detail.html が自動
    context_object_name = 'article'

FAQ

Q: テンプレートが見つからない (TemplateDoesNotExist)
A: DEBUG=True 状態でエラー画面に「探したパス一覧」が出る。配置ミス・APP_DIRS 設定漏れ・INSTALLED_APPS 登録漏れを確認。

Q: Jinja2 を使いたい
A: TEMPLATESBACKENDdjango.template.backends.jinja2.Jinja2 に。Django タグ ({% url %} 等) は使えなくなる。

Q: include vs extends の使い分け
A: extends は親レイアウトを継承して差し替える関係。include は同じ部品を埋め込む関係。ヘッダ・フッタ・カードは include、ページ全体は extends。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. Templateの使用準備
  2. Template の定義方法
  3. テンプレートの作成と共通化
  4. setting.pyにおけるテンプレートの設定
  5. テンプレートの名前の重複について
  6. 静的ファイルの読み込み
  7. if文
  8. テンプレートで定数を使用する方法
  9. aタグのhrefの記載方法