1.

Django Template 設定と使い方完全ガイド(DIRS / extends / block / フィルター)

編集
この記事の要点
  • Django Template Language (DTL) を使うには settings.py の TEMPLATES 設定が必須
  • 'DIRS': [BASE_DIR / 'templates'] でプロジェクト共通テンプレ置き場を指定
  • APP_DIRS: True で各アプリの <app>/templates/<app>/ を自動探索
  • 基本文法: {{ variable }}(変数)/ {% tag %}(タグ)/ {{ x|filter }}(フィルター)
  • 継承: {% extends 'base.html' %} + {% block content %} でレイアウト共通化

Django Template の全体像

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

ステップ 1: settings.py の TEMPLATES 設定

プロジェクト作成時にデフォルトで生成されていますが、DIRS を埋めるのが最初の作業です:

# mysite/settings.py
from pathlib import Path

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

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [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',
            ],
        },
    },
]
設定キー役割
BACKENDテンプレートエンジン本体(DTL or Jinja2)
DIRS共通テンプレディレクトリ(リスト順に探索)
APP_DIRSTrue で <app>/templates/ を自動探索
context_processors全テンプレに自動注入する変数群

ステップ 2: ディレクトリ構成

mysite/
├── manage.py
├── mysite/
│   ├── settings.py
│   └── urls.py
├── templates/              ← BASE_DIR / 'templates'(共通)
│   ├── base.html
│   └── partials/
│       ├── header.html
│       └── footer.html
└── blog/                   ← アプリ
    ├── views.py
    └── templates/          ← APP_DIRS で自動探索
        └── blog/           ← ★名前衝突回避のためアプリ名でネスト
            ├── index.html
            └── detail.html

重要: アプリの templates ディレクトリは blog/templates/blog/ のようにアプリ名でネストするのが慣例。複数アプリで index.html が衝突するのを防ぎます。

ステップ 3: ビューからテンプレートを呼ぶ

# blog/views.py
from django.shortcuts import render

def index(request):
    context = {
        'posts': Post.objects.all(),
        'site_name': 'My Blog',
    }
    return render(request, 'blog/index.html', context)
    #              ↑ APP_DIRS により blog/templates/blog/index.html を解決

DTL の基本文法

変数表示 {{ ... }}

<p>{{ post.title }}</p>
<p>{{ user.username }}</p>
<p>{{ posts.0.title }}</p>             {# リストのインデックス #}
<p>{{ post.created_at|date:"Y-m-d" }}</p>  {# フィルター適用 #}

タグ {% ... %}

{% if user.is_authenticated %}
  <p>こんにちは {{ user.username }} さん</p>
{% else %}
  <a href="{% url 'login' %}">ログイン</a>
{% endif %}

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

{% url 'blog:detail' post.pk %}    {# URL 解決 #}
{% csrf_token %}                   {# CSRF トークン #}
{% load humanize %}                {# テンプレタグライブラリ読込 #}

主要フィルター一覧

フィルター結果
date{{ d|date:"Y-m-d H:i" }}2026-06-10 12:34
length{{ list|length }}要素数
default{{ name|default:"匿名" }}空なら "匿名"
truncatechars{{ body|truncatechars:50 }}50文字 + ...
safe{{ html|safe }}エスケープ無効化(★危険、入力検証必須)
linebreaks{{ body|linebreaks }}改行を <br> / <p> に
floatformat{{ price|floatformat:2 }}1234.50
upper / lower{{ s|upper }}大文字化

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

共通レイアウトを base.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 'css/style.css' %}">
</head>
<body>
    {% include 'partials/header.html' %}

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

    {% include 'partials/footer.html' %}
</body>
</html>
{# blog/templates/blog/index.html #}
{% extends 'base.html' %}

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

{% block content %}
<h1>記事一覧</h1>
<ul>
  {% for post in posts %}
    <li><a href="{% url 'blog:detail' post.pk %}">{{ post.title }}</a></li>
  {% endfor %}
</ul>
{% endblock %}

include で部品化

{# 引数渡し #}
{% include 'partials/card.html' with post=post show_author=True %}

{# 親のコンテキスト引き継がない(独立) #}
{% include 'partials/card.html' with post=post only %}

{% load static %} と静的ファイル

{% load static %}

<link rel="stylesheet" href="{% static 'blog/css/post.css' %}">
<img src="{% static 'blog/img/logo.png' %}" alt="logo">
<script src="{% static 'blog/js/main.js' %}"></script>
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']  # 開発時の追加探索パス
STATIC_ROOT = BASE_DIR / 'staticfiles'    # collectstatic の出力先(本番)

テンプレートのキャッシュ(本番)

# settings.py(本番のみ。DEBUG=True ならコメントアウト)
TEMPLATES = [
    {
        # ...
        'APP_DIRS': False,  # cached.Loader 使用時は False
        'OPTIONS': {
            'loaders': [
                ('django.template.loaders.cached.Loader', [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                ]),
            ],
            'context_processors': [...],
        },
    },
]

FAQ

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

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

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

編集
Post Share
子ページ

子ページはありません

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

最近更新/作成されたページ