この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:7
ページ更新者:T
更新日時:2026-06-10 11:03:32

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

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>Django Template は <code>settings.py</code> の <code>TEMPLATES</code> 設定 + 各アプリの <code>templates/</code> ディレクトリで動く</li><li><strong>探索順</strong>: <code>DIRS</code> リスト → <code>APP_DIRS=True</code> ならインストール済アプリの <code>templates/</code></li><li><strong>継承</strong>: 親に <code>{% block content %}{% endblock %}</code>、子で <code>{% extends "base.html" %}</code> + <code>{% block content %}...{% endblock %}</code></li><li><strong>部分挿入</strong>: <code>{% include "header.html" %}</code> でファイルを差し込む</li><li><strong>HTML エスケープはデフォルト ON</strong>。<code>{{ var|safe }}</code> または <code>{% autoescape off %}</code> で解除(XSS 注意)</li><li>カスタムタグ/フィルタは <code>templatetags/</code> 配下で定義し、<code>{% load mytags %}</code> で読み込む</li></ul></td></tr></table>
<h2>settings.py — TEMPLATES 設定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># myproject/settings.py
import os
TEMPLATES = [
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.django.DjangoTemplates&apos;,
        &apos;DIRS&apos;: [os.path.join(BASE_DIR, &apos;templates&apos;)],   # プロジェクト全体共通
        &apos;APP_DIRS&apos;: True,                                # 各アプリの templates/ も探索
        &apos;OPTIONS&apos;: {
            &apos;context_processors&apos;: [
                &apos;django.template.context_processors.debug&apos;,
                &apos;django.template.context_processors.request&apos;,
                &apos;django.contrib.auth.context_processors.auth&apos;,
                &apos;django.contrib.messages.context_processors.messages&apos;,
            ],
        },
    },
]</code></pre>
<h2>ディレクトリ配置</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">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</code></pre>
<p>アプリの <code>templates/</code> 直下にファイルを置くと、他アプリの同名ファイルと衝突するため、<strong><code>templates/myapp/</code></strong> のようにアプリ名のサブディレクトリを挟むのが Django 標準のベストプラクティスです。</p>

<h2>ベーステンプレート (継承元)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# templates/base.html #}
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>{% block title %}MySite{% endblock %}</title>
  {% block extra_head %}{% endblock %}
</head>
<body>
  {% include "header.html" %}

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

  {% include "footer.html" %}

  {% block extra_js %}{% endblock %}
</body>
</html></code></pre>
<h2>子テンプレート (継承)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# templates/myapp/article_detail.html #}
{% extends "base.html" %}
{% load static %}

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

{% block extra_head %}
  <link rel="stylesheet" href="{% static &apos;myapp/article.css&apos; %}">
{% endblock %}

{% block content %}
  <article>
    <h1>{{ article.title }}</h1>
    <p class="meta">
      投稿者: {{ article.author.name }} / {{ article.created_at|date:"Y-m-d H:i" }}
    </p>
    <div class="body">
      {{ article.body|linebreaks }}
    </div>

    <h2>コメント ({{ comments|length }})</h2>
    {% for c in comments %}
      <div class="comment">
        <strong>{{ c.author }}</strong>: {{ c.body }}
      </div>
    {% empty %}
      <p>コメントはまだありません。</p>
    {% endfor %}
  </article>
{% endblock %}</code></pre>
<h2>部分挿入 (include)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# templates/header.html #}
<header>
  <a href="/">MySite</a>
  <nav>
    <a href="/articles/">記事</a>
    {% if user.is_authenticated %}
      <a href="/logout/">ログアウト ({{ user.username }})</a>
    {% else %}
      <a href="/login/">ログイン</a>
    {% endif %}
  </nav>
</header>

{# 呼び出し側 #}
{% include "header.html" %}

{# 変数を渡しながら include #}
{% include "card.html" with title="ようこそ" body=user.name only %}
{# only を付けると親コンテキストを渡さない #}</code></pre>
<h2>テンプレートタグ</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>タグ</th><th>意味</th></tr></thead>
<tbody>
<tr><td><code>{% if %}</code> / <code>{% elif %}</code> / <code>{% else %}</code></td><td>条件分岐</td></tr>
<tr><td><code>{% for x in xs %}</code> / <code>{% empty %}</code></td><td>ループ</td></tr>
<tr><td><code>{% url 'name' arg %}</code></td><td>URL 逆引き</td></tr>
<tr><td><code>{% csrf_token %}</code></td><td>CSRF トークン</td></tr>
<tr><td><code>{% block %}</code> / <code>{% extends %}</code></td><td>継承</td></tr>
<tr><td><code>{% include %}</code></td><td>部分挿入</td></tr>
<tr><td><code>{% load %}</code></td><td>カスタムタグライブラリ読込</td></tr>
<tr><td><code>{% with %}</code></td><td>一時変数</td></tr>
<tr><td><code>{% spaceless %}</code></td><td>タグ間の空白除去</td></tr>
<tr><td><code>{% autoescape on/off %}</code></td><td>HTML エスケープ切替</td></tr>
</tbody>
</table>

<h2>フィルタ (パイプ)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-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 }}                    {# 改行を <p><br> に #}
{{ user.name|escape }}                   {# 明示的にエスケープ #}</code></pre>
<h2>autoescape — XSS 対策</h2>

<p>Django テンプレートは<strong>デフォルトで全変数を HTML エスケープ</strong>します。<code><script>alert(1)</script></code> をそのまま埋めても文字列として表示されます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# 通常: エスケープされる #}
{{ user_input }}
{# → &lt;script&gt;alert(1)&lt;/script&gt; #}

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

{# ブロック単位で切替 #}
{% autoescape off %}
  {{ trusted_html }}
{% endautoescape %}</code></pre>
<p><strong>safe</strong> や <code>autoescape off</code> はサニタイズ済みデータにのみ使うこと。ユーザー入力に直接使うと XSS の温床になります。</p>

<h2>カスタムタグ・フィルタ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">myapp/
└── templatetags/
    ├── __init__.py
    └── myapp_extras.py</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># 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(&apos;myapp/_pagination.html&apos;)
def show_pagination(page_obj):
    return {&apos;page_obj&apos;: page_obj}</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# テンプレート側で読み込み #}
{% load myapp_extras %}

{{ order.amount|yen }}                  {# → 1,000,000 円 #}
&copy; {% current_year %} MySite
{% show_pagination page_obj %}</code></pre>
<h2>テンプレート探索順</h2>

<ol>
<li><code>TEMPLATES.DIRS</code> リスト(順序通り)</li>
<li><code>APP_DIRS=True</code> なら <code>INSTALLED_APPS</code> の順に各アプリの <code>templates/</code></li>
</ol>

<p>先に見つかったものが使われます。サードパーティアプリのテンプレートを<strong>上書きしたい</strong>場合は、<code>DIRS</code> に書いたプロジェクト共通 <code>templates/</code> 配下に同じパスで配置すれば優先されます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text"># 例: django.contrib.admin/templates/admin/index.html を上書きしたい
templates/
└── admin/
    └── index.html     ← これがあれば admin デフォルトを上書き</code></pre>
<h2>View からテンプレートを呼ぶ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># 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, &apos;myapp/article_detail.html&apos;, {
        &apos;article&apos;: article,
        &apos;comments&apos;: article.comments.all(),
    })

# CBV の場合
from django.views.generic import DetailView
class ArticleDetailView(DetailView):
    model = Article
    template_name = &apos;myapp/article_detail.html&apos;    # 省略時は myapp/article_detail.html が自動
    context_object_name = &apos;article&apos;</code></pre>
<h2>FAQ</h2>

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

<p><strong>Q: Jinja2 を使いたい</strong><br />
A: <code>TEMPLATES</code> の <code>BACKEND</code> を <code>django.template.backends.jinja2.Jinja2</code> に。Django タグ (<code>{% url %}</code> 等) は使えなくなる。</p>

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