djangoでテンプレートファイルを管理する時、置き場所の候補はたぶん2つ考えられます。(*1)
- project ディレクトリの直下に
templates
ディレクトリを作り、その下に application ごとのサブディレクトリを作る - 各 application ディレクトリの直下に
templates
ディレクトリを作る
ただ、どういう風に管理するのがベスト(というか一般的)なのかということについて
ググったけどもなかなかこれだ!というのが出てきませんでした。
ので、色々考えてみましたという話。
使用している Django のバージョンは 1.5。
はじめは application 直下に管理していた
これまでは application ディレクトリの下にtemplates
ディレクトリを作り、
その下にそれぞれのアプリで必要なテンプレートを作って管理してました。
. ├── app1 │ ├── templates │ │ └── index.html │ ├── urls.py │ └── views.py ├── app2 │ ├── templates │ │ └── index.html │ ├── urls.py │ └── views.py ├── manage.py └── proj ├── settings.py ├── urls.py └── wsgi.py
(__init__.py
などは省略してます)
上の管理方法によるメリット
application ごとにtemplates
ディレクトリを用意しているので、
View でテンプレートを使用するときにアプリケーション名(正しくは「アプリケーションのディレクトリ名」か)を記述する必要がありません。
つまり、こうです。
views.py
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render class AppView(View): template_name = 'index.html' def get(self, request): return render(request, self.template_name)
template_name='app/index.html'
などとしているドキュメントを良く見ますが、
これだと View を新しく定義するたびにそれぞれにアプリケーション名を書く必要があり、
(あんまりないとはいえ)アプリケーション名を変更したい場合に影響範囲が大きいと考えたのです。
上の管理方法によるデメリット
ただ、この管理方法には重大な欠点があります。
それは、次のような場合です。
上述したようにapp1
、app2
というアプリケーションがあり、
それぞれに index.html
という同一ファイル名のテンプレートが定義されているとします。
settings.py (抜粋)
INSTALLED_APPS = ( 'django.contrib.auth', ...(略)... 'app1', 'app2', )
urls.py
from django.conf.urls import patterns, include, url urlpatterns = patterns('', url(r'^app1/', include('app1.urls')), url(r'^app2/', include('app2.urls')), )
urls.py (app1)
# -*- coding: utf-8 -*- from django.conf.urls import patterns, url from .views import App1View urlpatterns = patterns('', url(r'^', App1View.as_view()), )
urls.py (app2)
# -*- coding: utf-8 -*- from django.conf.urls import patterns, url from .views import App2View urlpatterns = patterns('', url(r'^', App2View.as_view()), )
views.py (app1)
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render class App1View(View): template_name = 'index.html' def get(self, request): return render(request, self.template_name)
views.py (app2)
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render class App2View(View): template_name = 'index.html' def get(self, request): return render(request, self.template_name)
index.html (app1)
<html> <head></head> <body> this is app1 </body> </html>
index.html (app2)
<html> <head></head> <body> this is app2 </body> </html>
さて、この状態でプロジェクトを実行してみます。
$ python manage.py runserver
はじめに、http://localhost:8000/app1/ にアクセスしてみます。
はい、問題ないですね。
app1/templates
のindex.html
が表示されています。
それでは、http://localhost:8000/app2/ にアクセスしてみます。
あれ...?
app2
ではなくapp1
のtemplates
ディレクトリの方のindex.html
から読み込んでいるみたいですね。
つまり、複数のアプリケーションで同一テンプレート名を定義すると、先に見つかった方が使用されてしまうんです。
index.html
等はどのアプリケーションでも一般的に使用されるテンプレート名だと思うので、
この名前を異なるアプリケーション間でユニークになるようにするというのは現実的ではありません。
というわけで、アプリケーションディレクトリ直下に置いて管理するのはやめようと思いました。
project 直下にテンプレートを移動する
というわけで、templates
ディレクトリは project 直下に置くように変更します。
先ほどの例では、ディレクトリ構成を以下のように修正します。
. ├── app1 │ ├── urls.py │ └── views.py ├── app2 │ ├── urls.py │ └── views.py ├── manage.py ├── manage.py └── proj ├── __init__.py ├── settings.py ├── templates │ ├── app1 │ │ └── index.html │ └── app2 │ └── index.html ├── urls.py └── wsgi.py
そうすると、上で述べたように、View内でtemplate_name
を記載する時にはアプリケーション名が必要です。
views.py (app1)
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render class App1View(View): # アプリケーション名が必要 template_name = 'app1/index.html' def get(self, request): return render(request, self.template_name)
1つのアプリケーションで使用する View が増えると、そのたびにtemplate_name
にアプリケーション名を書かないといけません。
これをどうにかしたい。
改善策1: アプリケーション名は1ヶ所にまとめる
「Viewごとにtemplate_name
にアプリケーション名直書き」状態を回避する方法を考えてみます。
一般的には、定数としてくくりだしてしまって、template_name
でそれを利用する方法ですね。
というわけで今回は__init__.py
に
# -*- coding: utf-8 -*- APP_LABEL = 'app1'
と書いてみました。
そしてView側からこれを import します。
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render from . import APP_LABEL class App1View(View): template_name = '%s/index.html' % APP_LABEL def get(self, request): return render(request, self.template_name)
これで、Viewのあちこちにアプリケーション名埋め込んで保守が大変、ということはなくなりました。
今回の例では、APP_LABEL
はもちろんviews.py
の先頭で定義しちゃっても問題ないんですが
その他のファイルで使う可能性を考えて__init__.py
に書いてみました。
改善策2: urls.py の app_name を使用する
せっかくなので、もう1個別の方法を考えてみます。
以前、テンプレートのタグ内でアプリケーション名を取得する方法について書きました。
これを View にも使ってみます。
まず、urls.py
でapp_name
を定義しましょう。
urls.py
from django.conf.urls import patterns, include, url urlpatterns = patterns('', url(r'^app1/', include('app1.urls', app_name='app1')), url(r'^app2/', include('app2.urls')), )
そして View 側を次のように変更します。
views.py
# -*- coding: utf-8 -*- from django.views.generic.base import View from django.shortcuts import render class App1View(View): template_name = '{app_name}/index.html' def get(self, request): self.template_name = self.template_name.format(app_name=request.resolver_match.app_name) return render(request, self.template_name)
これでもいいですね。
どっちがいいのか?
1 でも 2 でもとりあえず View のあちこちにアプリケーション名を直書きするという事態は回避できています。
どちらがいいか、今のところは判断がつかないんですが、
- 1 だと
app_name
の他にAPP_LABEL
を定義する必要がある - 2 だと(逆に)アプリケーションのディレクトリ名=
app_name
という制約がある - 2 だと毎回 View の先頭で
{app_name}
を置換する必要がある
というあたりが両者を比較した時に感じたところです。
個人的には特に最後に挙げたものが気になるかな...って感じなので
しばらくはAPP_LABEL
に書く方法で実装してみようかなと思いました。
注記
(*1)
1, 2の方法はsettings.py
のTEMPLATE_LOADERS
に
以下のように2つの loader を定義していることが前提となっています。
settings.py
TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', # 1. の方法を使う場合 'django.template.loaders.app_directories.Loader', # 2. の方法を使う場合 )
また、1 のproject直下に配置する場合は
同じくsettings.py
のTEMPLATE_DIRS
に絶対パスで指定する必要があります。
# Django settings for proj project. import os ...(略)... ROOT_PATH = os.path.dirname(__file__) TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. os.path.join(ROOT_PATH, 'templates'), )
参考: https://docs.djangoproject.com/en/1.5/ref/templates/api/#loader-types
(2015/11/06追記)
Django 1.8 から設定方法が変わっているみたいです。
https://docs.djangoproject.com/en/1.8/topics/templates/
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { # ... some options here ... }, }, ]
DIRS
defines a list of directories where the engine should look for template source files, in search order.
APP_DIRS
tells whether the engine should look for templates inside installed applications. Each backend defines a conventional name for the subdirectory inside applications where its templates should be stored.