DjangoでTemplateDoesNotExistと言われたら

別のブログ記事用に、Djangoで簡単なアプリを作った。モデルがなく、単にView関数を呼び出してTemplateを出すだけ。

from django.shortcuts import render
from django.views.generic import View

# Create your views here.

class IndexView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'myapp/index.html')

するとエラーが出た。

django.template.exceptions.TemplateDoesNotExist: myapp/index.html

このとき何をチェックすべきだろうか。

Templateのパスを確認する

自分がよく間違えるのが、Templateのパスの書き方。
公式ドキュメントを読んで、DjangoのTemplateの探し方を理解しておく必要がある。

Templateの探し方は、プロジェクトのsettings.pyの中にあるTEMPLATESの項目で規定されている。
デフォルトでは、プロジェクトを作ったあとは以下のような状態になっているはず。(Django2.1の場合)

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

DIRSに何も入っておらず、APP_DIRSがTrueになっているので、Djangoプロジェクト内の各アプリケーションの配下にあるtemplatesという名前のディレクトリ内を探索する。
そのため、単にアプリケーション配下にTemplateファイルを置いてもだめ。templatesというディレクトリを作る必要がある。
各アプリケーションのtemplatesディレクトリが探索の起点となるので、myapp/templates/myapp/index.html に置いたTemplateファイルをViewの中で指定するには、単にmyapp/index.html と書けばよい。

なお、探索の際はどのアプリケーションかを考慮せずに、最初にパスがマッチしたら探索終了となる仕様になっている。そのため、templates内にさらにディレクトリを切るなどして、名前空間を分けることが推奨されている。

ただ、今回の場合はmyapp/templates/myapp/index.htmlとなるようにファイルを作っていたので、パスの問題ではなかった。

INSTALLED_APPSを確認する

上記のTemplate探索にはもうひとつ前提がある。「各アプリケーションの配下にあるtemplatesディレクトリ」を探索するということは、アプリケーションと認識されていなければ探索されないということだ。
今回はそもそもここに原因があった。settings.pyのINSTALLED_APPSにmyappを登録するのを忘れていた。

INSTALLED_APPSには、そのプロジェクト内の全てのアプリケーションを登録しなければならない。
アプリケーションがあるパッケージ名をそのまま書いても動くことは動くが、推奨されているのは各アプリケーションのConfigurationクラスを登録することだ。
今回の場合、myapp.apps.MyappConfigを登録することで、無事にTemplateが表示されるようになった。