タイトル: setting.pyにおけるテンプレートの設定
SEOタイトル: Django settings.py TEMPLATES 設定完全ガイド(DIRS / APP_DIRS / context_processors)
| この記事の要点 |
|
TEMPLATES の基本構造
# myproject/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,
'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',
],
},
},
]
TEMPLATES はリストオブディクトです。1 要素 = 1 テンプレートエンジンの設定。複数エンジンを並べて先頭から探索する形になります。
各キーの意味
| キー | 意味 | 例 |
|---|---|---|
BACKEND | 使用するテンプレートエンジンのクラス | django.template.backends.django.DjangoTemplates / django.template.backends.jinja2.Jinja2 |
NAME | このエンジンの識別名(省略時はクラス名から自動) | 'django' |
DIRS | テンプレートを探すディレクトリのリスト | [BASE_DIR / 'templates'] |
APP_DIRS | 各 INSTALLED_APPS の templates/ も探すか | True |
OPTIONS | エンジン固有設定 | context_processors, loaders, debug, autoescape ... |
テンプレート探索順
Django は次の順でテンプレートを探します:
DIRSに書かれたディレクトリを順番にAPP_DIRS=TrueならINSTALLED_APPS順に各アプリのtemplates/- 見つかった最初のものを使う
myproject/
├── templates/ ← DIRS で指定
│ ├── base.html
│ └── home.html
├── polls/
│ └── templates/
│ └── polls/ ← APP_DIRS=True で自動探索
│ ├── index.html
│ └── detail.html
└── blog/
└── templates/
└── blog/
└── post.html
アプリ名のサブディレクトリで包むのが定石(polls/templates/polls/index.html)。これは複数アプリで同名テンプレートが衝突しないようにするためです。
DIRS の書き方(Pathlib)
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# 単一ディレクトリ
'DIRS': [BASE_DIR / 'templates'],
# 複数ディレクトリ
'DIRS': [
BASE_DIR / 'templates',
BASE_DIR / 'shared_templates',
'/var/www/global_templates',
],
# 旧形式(Django 3.0 以前)
import os
'DIRS': [os.path.join(BASE_DIR, 'templates')],
context_processors(全テンプレートに変数注入)
すべてのテンプレートで自動的に使える変数を提供する関数群です:
| プロセッサ | 提供する変数 |
|---|---|
django.template.context_processors.debug | debug, sql_queries(DEBUG=True 時のみ) |
django.template.context_processors.request | request(HttpRequest オブジェクト) |
django.contrib.auth.context_processors.auth | user, perms |
django.contrib.messages.context_processors.messages | messages |
django.template.context_processors.media | MEDIA_URL |
django.template.context_processors.static | STATIC_URL |
django.template.context_processors.csrf | csrf_token |
django.template.context_processors.tz | TIME_ZONE |
django.template.context_processors.i18n | LANGUAGES, LANGUAGE_CODE |
カスタム context_processor を作る
# myproject/context_processors.py
def site_settings(request):
return {
'SITE_NAME': 'My Awesome Site',
'SUPPORT_EMAIL': 'support@example.com',
'UNREAD_COUNT': request.user.notifications.unread().count() if request.user.is_authenticated else 0,
}
# settings.py
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'myproject.context_processors.site_settings',
],
},
},
]
# 以後、全テンプレートで {{ SITE_NAME }} が使える
カスタムタグ・フィルタの登録
# myapp/templatetags/__init__.py(空ファイル必須)
# myapp/templatetags/my_tags.py
from django import template
register = template.Library()
@register.filter
def upper_first(value):
return value[:1].upper() + value[1:]
@register.simple_tag
def greeting(name):
return f"Hello, {name}!"{# template.html #}
{% load my_tags %}
{{ name|upper_first }}
{% greeting "Alice" %}
Django Templates と Jinja2 の併用
Django 1.8+ から複数バックエンドを登録できます。Jinja2 は速くて柔軟ですが、Django テンプレートが {{ user.is_authenticated }} などフレームワーク統合は強い:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [BASE_DIR / 'jinja2'],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'myproject.jinja2.environment',
},
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [...],
},
},
]
# 探索順は先頭から。Jinja2 の jinja2/ に同名 .html があれば優先
Jinja2 では jinja2/ サブディレクトリが標準(Django Templates の templates/ と区別するため)。
OPTIONS の主要設定
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [...],
# デフォルト True、False で disable
'debug': DEBUG,
# HTML エスケープを autoescape するか(デフォルト True)
'autoescape': True,
# 不明な変数を解決するときの値(デフォルトは空文字)
'string_if_invalid': 'INVALID: %s' if DEBUG else '',
# カスタムローダー(APP_DIRS=False のとき有効)
'loaders': [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]),
],
# カスタムタグライブラリ
'libraries': {
'my_tags': 'myapp.templatetags.my_tags',
},
# 組み込みタグライブラリの追加
'builtins': ['myapp.templatetags.my_tags'],
},
},
]
cached.Loader でパフォーマンス
本番では cached.Loader を使うとテンプレートのパースをキャッシュして高速化。ただし APP_DIRS と排他なので明示的に loaders を書く必要があります:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
# 'APP_DIRS': True, # ← コメントアウト
'OPTIONS': {
'context_processors': [...],
'loaders': [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]),
],
},
},
]
テンプレート継承パターン
{# templates/base.html #}
{% block title %}{{ SITE_NAME }}{% endblock %}
{% load static %}
{% include "partials/_header.html" %}
{% block content %}{% endblock %}
{% include "partials/_footer.html" %}
{# templates/polls/index.html #}
{% extends "base.html" %}
{% block title %}投票一覧 | {{ SITE_NAME }}{% endblock %}
{% block content %}
投票一覧
{% for q in latest_questions %}
- {{ q.question_text }}
{% endfor %}
{% endblock %}
Bootstrap / Tailwind 連携
{# Bootstrap CDN #}
{% load static %}
{# django-crispy-forms + crispy-bootstrap5 で美フォーム #}
# pip install django-crispy-forms crispy-bootstrap5
# settings.py
INSTALLED_APPS += ['crispy_forms', 'crispy_bootstrap5']
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = 'bootstrap5'
# template
{% load crispy_forms_tags %}
環境別 設定の切替
# 本番では cached.Loader、開発では APP_DIRS=True
if DEBUG:
TEMPLATES[0]['APP_DIRS'] = True
TEMPLATES[0]['OPTIONS'].pop('loaders', None)
else:
TEMPLATES[0]['APP_DIRS'] = False
TEMPLATES[0]['OPTIONS']['loaders'] = [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]),
]
# string_if_invalid を開発のみ
if DEBUG:
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = 'INVALID: %s'
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
TemplateDoesNotExist: polls/index.html | DIRS / APP_DIRS / アプリ登録漏れ | 探索パスを print(settings.TEMPLATES) 等で確認 |
{{ request.user }} が動かない | context_processors に request / auth が無い | 2 つ追加 |
{% csrf_token %} でエラー | context_processors の csrf が必要(標準で入る) | RequestContext を使う / render() を使う |
| cached.Loader を有効化したら変更が反映されない | サーバ再起動が必要 | 開発中は APP_DIRS=True、本番のみ cached |
| テンプレートタグが見つからない | templatetags/__init__.py 無し | 空ファイルでも置く |
FAQ
Q: BASE_DIR / 'templates' と os.path.join(BASE_DIR, 'templates') どっちが良い?
A: 新規プロジェクトは Pathlib(前者)。Django 3.1+ で startproject も Pathlib に変わった。可読性も高い。
Q: テンプレートを動的に追加したい
A: DIRS に動的にパスを追加するか、カスタムテンプレートローダーを実装。プラグイン的に登録するなら libraries + builtins。
Q: 1 つのアプリで複数のテンプレートディレクトリを持ちたい
A: アプリ内に templates/ を作るのが標準。さらに分けたければ DIRS で追加。