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

タイトル: Templateの使用準備
SEOタイトル: Django Template 設定と使い方完全ガイド(DIRS / extends / block / フィルター)

この本文には実行可能なタグ等が含まれるため、安全のため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 Language (DTL) を使うには <strong><code>settings.py</code> の TEMPLATES 設定</strong>が必須</li><li><strong><code>'DIRS': [BASE_DIR / 'templates']</code></strong> でプロジェクト共通テンプレ置き場を指定</li><li><strong><code>APP_DIRS: True</code></strong> で各アプリの <code><app>/templates/<app>/</code> を自動探索</li><li>基本文法: <strong><code>{{ variable }}</code></strong>(変数)/ <strong><code>{% tag %}</code></strong>(タグ)/ <strong><code>{{ x|filter }}</code></strong>(フィルター)</li><li>継承: <strong><code>{% extends 'base.html' %}</code></strong> + <strong><code>{% block content %}</code></strong> でレイアウト共通化</li></ul></td></tr></table>
<h2>Django Template の全体像</h2>

<p>Django Template Language (DTL) は Django 標準のテンプレートエンジン。HTML に <code>{% tag %}</code> や <code>{{ variable }}</code> を埋め込み、ビューから渡されたコンテキストで描画します。Jinja2 も切り替え可能ですが、DTL がデフォルトで完備されているので本記事は DTL を扱います。</p>

<h2>ステップ 1: settings.py の TEMPLATES 設定</h2>

<p>プロジェクト作成時にデフォルトで生成されていますが、<strong>DIRS</strong> を埋めるのが最初の作業です:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># mysite/settings.py
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

TEMPLATES = [
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.django.DjangoTemplates&apos;,
        &apos;DIRS&apos;: [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>
<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>BACKEND</td><td>テンプレートエンジン本体(DTL or Jinja2)</td></tr>
<tr><td>DIRS</td><td>共通テンプレディレクトリ(リスト順に探索)</td></tr>
<tr><td>APP_DIRS</td><td>True で <code><app>/templates/</code> を自動探索</td></tr>
<tr><td>context_processors</td><td>全テンプレに自動注入する変数群</td></tr>
</tbody>
</table>

<h2>ステップ 2: ディレクトリ構成</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">mysite/
├── manage.py
├── mysite/
│   ├── settings.py
│   └── urls.py
├── templates/              ← BASE_DIR / &apos;templates&apos;(共通)
│   ├── base.html
│   └── partials/
│       ├── header.html
│       └── footer.html
└── blog/                   ← アプリ
    ├── views.py
    └── templates/          ← APP_DIRS で自動探索
        └── blog/           ← ★名前衝突回避のためアプリ名でネスト
            ├── index.html
            └── detail.html</code></pre>
<p><strong>重要</strong>: アプリの templates ディレクトリは <code>blog/templates/blog/</code> のように<strong>アプリ名でネスト</strong>するのが慣例。複数アプリで <code>index.html</code> が衝突するのを防ぎます。</p>

<h2>ステップ 3: ビューからテンプレートを呼ぶ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># blog/views.py
from django.shortcuts import render

def index(request):
    context = {
        &apos;posts&apos;: Post.objects.all(),
        &apos;site_name&apos;: &apos;My Blog&apos;,
    }
    return render(request, &apos;blog/index.html&apos;, context)
    #              ↑ APP_DIRS により blog/templates/blog/index.html を解決</code></pre>
<h2>DTL の基本文法</h2>

<h3>変数表示 <code>{{ ... }}</code></h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><p>{{ post.title }}</p>
<p>{{ user.username }}</p>
<p>{{ posts.0.title }}</p>             {# リストのインデックス #}
<p>{{ post.created_at|date:"Y-m-d" }}</p>  {# フィルター適用 #}</code></pre>
<h3>タグ <code>{% ... %}</code></h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{% if user.is_authenticated %}
  <p>こんにちは {{ user.username }} さん</p>
{% else %}
  <a href="{% url &apos;login&apos; %}">ログイン</a>
{% endif %}

{% for post in posts %}
  <li>{{ post.title }} ({{ forloop.counter }}/{{ posts|length }})</li>
{% empty %}
  <li>記事はまだありません</li>
{% endfor %}

{% url &apos;blog:detail&apos; post.pk %}    {# URL 解決 #}
{% csrf_token %}                   {# CSRF トークン #}
{% load humanize %}                {# テンプレタグライブラリ読込 #}</code></pre>
<h3>主要フィルター一覧</h3>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>フィルター</th><th>例</th><th>結果</th></tr></thead>
<tbody>
<tr><td><code>date</code></td><td><code>{{ d|date:"Y-m-d H:i" }}</code></td><td>2026-06-10 12:34</td></tr>
<tr><td><code>length</code></td><td><code>{{ list|length }}</code></td><td>要素数</td></tr>
<tr><td><code>default</code></td><td><code>{{ name|default:"匿名" }}</code></td><td>空なら "匿名"</td></tr>
<tr><td><code>truncatechars</code></td><td><code>{{ body|truncatechars:50 }}</code></td><td>50文字 + ...</td></tr>
<tr><td><code>safe</code></td><td><code>{{ html|safe }}</code></td><td>エスケープ無効化(★危険、入力検証必須)</td></tr>
<tr><td><code>linebreaks</code></td><td><code>{{ body|linebreaks }}</code></td><td>改行を <br> / <p> に</td></tr>
<tr><td><code>floatformat</code></td><td><code>{{ price|floatformat:2 }}</code></td><td>1234.50</td></tr>
<tr><td><code>upper / lower</code></td><td><code>{{ s|upper }}</code></td><td>大文字化</td></tr>
</tbody>
</table>

<h2>テンプレート継承 (extends / block)</h2>

<p>共通レイアウトを <code>base.html</code> に定義し、各ページで上書きします:</p>
<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 #}
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{{ site_name }}{% endblock %}</title>
    <link rel="stylesheet" href="{% static &apos;css/style.css&apos; %}">
</head>
<body>
    {% include &apos;partials/header.html&apos; %}

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

    {% include &apos;partials/footer.html&apos; %}
</body>
</html></code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# blog/templates/blog/index.html #}
{% extends &apos;base.html&apos; %}

{% block title %}記事一覧 | {{ block.super }}{% endblock %}

{% block content %}
<h1>記事一覧</h1>
<ul>
  {% for post in posts %}
    <li><a href="{% url &apos;blog:detail&apos; post.pk %}">{{ post.title }}</a></li>
  {% endfor %}
</ul>
{% 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">{# 引数渡し #}
{% include &apos;partials/card.html&apos; with post=post show_author=True %}

{# 親のコンテキスト引き継がない(独立) #}
{% include &apos;partials/card.html&apos; with post=post only %}</code></pre>
<h2>{% load static %} と静的ファイル</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{% load static %}

<link rel="stylesheet" href="{% static &apos;blog/css/post.css&apos; %}">
<img src="{% static &apos;blog/img/logo.png&apos; %}" alt="logo">
<script src="{% static &apos;blog/js/main.js&apos; %}"></script></code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># settings.py
STATIC_URL = &apos;/static/&apos;
STATICFILES_DIRS = [BASE_DIR / &apos;static&apos;]  # 開発時の追加探索パス
STATIC_ROOT = BASE_DIR / &apos;staticfiles&apos;    # collectstatic の出力先(本番)</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-python"># settings.py(本番のみ。DEBUG=True ならコメントアウト)
TEMPLATES = [
    {
        # ...
        &apos;APP_DIRS&apos;: False,  # cached.Loader 使用時は False
        &apos;OPTIONS&apos;: {
            &apos;loaders&apos;: [
                (&apos;django.template.loaders.cached.Loader&apos;, [
                    &apos;django.template.loaders.filesystem.Loader&apos;,
                    &apos;django.template.loaders.app_directories.Loader&apos;,
                ]),
            ],
            &apos;context_processors&apos;: [...],
        },
    },
]</code></pre>
<h2>FAQ</h2>

<p><strong>Q: テンプレートが見つからないと言われる</strong><br />
A: <code>blog/templates/blog/index.html</code> のように<strong>アプリ名でネスト</strong>しているか確認。また INSTALLED_APPS に登録され <code>APP_DIRS: True</code> である必要があります。</p>

<p><strong>Q: Jinja2 を使いたい</strong><br />
A: TEMPLATES に <code>BACKEND: 'django.template.backends.jinja2.Jinja2'</code> を追加。DTL と並行利用も可。</p>

<p><strong>Q: テンプレートで Python 関数を直接呼びたい</strong><br />
A: 不可(意図的制限)。<strong>カスタムテンプレートタグ / フィルター</strong>を <code>templatetags/</code> に作って <code>{% load %}</code> して使います。</p>