できない.dev

Django で「TemplateDoesNotExist」が出てテンプレートを読み込めない

TemplateDoesNotExist は、テンプレート名がどの探索先にも見つからないときに出る。
エラー画面の「Template-loader postmortem」に探したパスが並ぶので、DIRS・APP_DIRS・INSTALLED_APPS と配置を突き合わせる。

公開:

要約

Django のテンプレートは、settings.py の TEMPLATES に書いた探索先を順に調べて見つける。
どこにも無ければ次の例外になる。

django.template.exceptions.TemplateDoesNotExist: index.html

DEBUG = True ならエラー画面に「Template-loader postmortem」が表示され、ローダーごとに実際に探したパスと Source does not exist が並ぶ。
置いたつもりの場所がこの一覧に無ければ、探索先の設定が足りない。
一覧にあるのに見つからなければ、ファイル名かディレクトリの階層が違う。

よくある原因

  1. DIRS が空: startproject が作る設定は "DIRS": [] なので、プロジェクト直下に templates/ を作っただけでは探索されない。
  2. アプリのディレクトリが探索対象外: APP_DIRS が True のとき、Django は INSTALLED_APPS に登録された各アプリの templates サブディレクトリを探す。
    アプリが未登録、または APP_DIRS が False だとそこは見られない。
  3. 名前空間の階層が合っていない: render() に渡す名前は、探索先ディレクトリからの相対パスだ。blog/templates/index.html に置いたファイルは "index.html" で見つかり、"blog/index.html" では見つからない。
  4. 継承元や部品が無い: {% extends "base.html" %} の base.html が無い場合、例外に出る名前は render() に渡した名前ではなく base.html になる。

解決策

1. プロジェクト共通のディレクトリを DIRS に登録する

# settings.py
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            # startproject が生成した context_processors はそのまま残す
        },
    },
]

BASE_DIR は startproject が settings.py の先頭で定義しており、manage.py のあるディレクトリを指す。

2. アプリを登録し APP_DIRS を有効にする

アプリ内のテンプレートを使うなら、アプリを INSTALLED_APPS に入れ、APP_DIRS を True にする。

INSTALLED_APPS = [
    "blog.apps.BlogConfig",
    # ...
]

3. アプリ名のサブディレクトリで名前空間を切る

公式ドキュメントは、テンプレートをアプリごとのサブディレクトリに整理する方法を推奨している。

blog/
└── templates/
    └── blog/
        └── index.html
from django.shortcuts import render
 
def index(request):
    return render(request, "blog/index.html")

名前空間を切らずに複数のアプリへ同じ index.html を置くと、INSTALLED_APPS で先に並んだアプリのものが使われる。
エラーにならないまま別のテンプレートが表示されるので、最初から階層を分けておく。

4. 例外に出ている名前を読む

例外やエラー画面の見出しに出ているテンプレート名が、ビューで指定した名前と違うかを確認する。
違うなら、そのテンプレートを {% extends %} か {% include %} している箇所が原因だ。

{% extends "base.html" %}

継承元を templates/base.html に置くか、実際の配置に合わせて "layouts/base.html" のように名前を直す。

この記事は役立ちましたか?