revision-up-to: | 8961 (1.0) |
---|
このドキュメントでは、 Django のテンプレートシステムを技術的な側面、すなわ ちどのように動作し、どうやって拡張するかという観点から解説します。テンプレー ト言語の構文リファレンスを探しているのなら、 Django テンプレート言語 を参照 してください。
Django テンプレートシステムを他のアプリケーションの部品として使いたい場合、 すなわち Django フレームワークの他の部分は必要ない場合、このドキュメントの 後の方にある 設定 の節に必ず目を通して下さい。
テンプレート (template) とは、 Django テンプレート言語を使ってマークアッ プしたテキストドキュメントや Python 文字列です。テンプレートには ブロックタグ (block tag) や 変数 (variable) を入れられます。
ブロックタグ はテンプレート中で何らかの処理を行う部分を表すシンボルです。
とはいえ、この定義はいささか曖昧ですね。例えば、ブロックタグを使うと、何ら かのコンテンツを出力したり、制御構造 (“if” 文や “for” ループ) を記述したり、 データベースからコンテンツを取り出したり、他のテンプレートタグにアクセスし たりします。
ブロックタグは "{%" と "%}" で囲みます。
ブロックタグを使ったテンプレートの例を示します:
{% if is_logged_in %}Thanks for logging in!{% else %}Please log in.{% endif %}
変数 はテンプレート中で何らかの値を出力する部分を表すシンボルです。
変数は "{{" と "}}" で囲みます。
変数を使ったテンプレートの例を示します。:
My first name is {{ first_name }}. My last name is {{ last_name }}.
コンテキスト (context) はテンプレートに渡される「変数名」から「変数の値」 へのマッピングです。
テンプレートの レンダリング (rendering) では、コンテキストから値を取り 出して変数という「穴」に埋め、全てのブロックタグを実行します。
Python でテンプレートを使うには、以下の 2 段階のプロセスを踏みます:
Template オブジェクトを作成するには、直接インスタンスを生成する方法が最 も簡単です。 Template クラスは django.template.Template にあります。 コンストラクタは引数に生のテンプレートコードを取ります:
>>> from django.template import Template
>>> t = Template("My name is {{ my_name }}.")
>>> print t
<django.template.Template instance>
舞台裏
このシステムは Template オブジェクトを生成するときに生のテンプレー トコードを一度しか解析しません。コンパイル後のテンプレートはパフォーマ ンス上の目的から "node" データ構造に保存されます。
テンプレートコードの解析自体もきわめて高速です。解析のほとんどは短い正 規表現を一つ呼び出すだけで終わります。
コンパイル済みの Template オブジェクトを作成したら、オブジェクトを使っ てコンテキストをレンダリングできるようになります。 Context クラスは django.template.Context にあります。コンテキストクラスのコンストラクタ は単一の (省略可能な) 引数として、変数名を変数の値に対応づける辞書をとりま す。コンテキストの値でテンプレートを「埋める」には、 Template オブジェ クトの render() メソッドを使います:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ my_name }}.")
>>> c = Context({"my_name": "Adrian"})
>>> t.render(c)
"My name is Adrian."
>>> c = Context({"my_name": "Dolores"})
>>> t.render(c)
"My name is Dolores."
変数名には、任意の文字 (A-Z)、数字 (0-9)、アンダースコアまたはドットしか 使えません。
ドットはテンプレートレンダリングにおいて特殊な意味を持ちます。変数名中の ドットは、 値の照合 (lookup) を意味します。もっと詳しく言えば、テンプレー トシステムが変数名中にドットを見つけた場合、以下の順で値の照合を試みます:
テンプレートシステムは最初に成功した照合結果を使います。いわば短絡的ロジッ ク (short-circuit logic) です。
いくつか例を示します:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."
>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."
>>> class PersonClass2:
... def first_name(self):
... return "Samantha"
>>> p = PersonClass2()
>>> t.render(Context({"person": p}))
"My name is Samantha."
>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."
メソッドの照合は他の照合よりもちょっと複雑です。いくつか心に留めておくべき ことを挙げておきます:
メソッドの照合中にメソッドが送出した例外に silent_variable_failure という属性があり、かつ値が True に設 定されている場合を除き、例外はそのまま伝播します。例外が silent_variable_failure という属性を持つ場合、変数は空文字列にレ ンダリングされます。例えば:
>>> t = Template("My name is {{ person.first_name }}.")
>>> class PersonClass3:
... def first_name(self):
... raise AssertionError, "foo"
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo
>>> class SilentAssertionError(Exception):
... silent_variable_failure = True
>>> class PersonClass4:
... def first_name(self):
... raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."
全ての Django データベース API の DoesNotExist 例外の基底クラスで ある django.core.exceptions.ObjectDoesNotExist では silent_variable_failure = True になっているので注意してください。 つまり、 Django テンプレートと Django モデルオブジェクトを使っている 限り、 DoesNotExist 例外が出ても暗黙のうちに失敗するだけなのです。
メソッド呼び出しがうまくいくのは、メソッドに必須の引数が存在しない場 合だけです。必須の引数がある場合、メソッドは呼び出されず、次の照合タ イプ (リストのインデクス指定による照合) に移ります。
言うまでもないことですが、メソッドによっては副作用があります。 副作用のあるメソッドをテンプレートからアクセスできるようにしておくと、 間抜けな結果になったりセキュリティホールを作ったりしてしまうことがある ので注意して下さい。
分かりやすい例に、 Django モデルオブジェクトの delete() メソッド があります。テンプレートシステムでは、以下のような書き方を許すべきで はありません:
大事なデータを消しちゃいますよ。 {{ data.delete }}
このような操作を防ぐには、メソッドに alters_data 関数属性を設定し ます。テンプレートシステムは、 alters_data=True の設定されたメソッ ドを実行しません。 Django モデルオブジェクトが動的に生成する delete() および save() メソッドには alters_data=True が自 動的に設定されます。 alters_data=True の例を以下に示します:
def sensitive_function(self):
self.database_record.delete()
sensitive_function.alters_data = True
基本的に、変数が存在しなければ、 テンプレートシステムは TEMPLATE_STRING_IF_INVALID の設定値を挿入します。この値のデフォルトは '' (空文字列) です。
無効な値に対してフィルタが適用されるのは、 TEMPLATE_STRING_IF_INVALID の値が '' に設定されている場合だ けです。 TEMPLATE_STRING_IF_INVALID が '' 以外の値になってい ると、フィルタは無視されます。
ただし、 if, for, regroup といったテンプレートタグの中では、少 し違った挙動になります。これらのテンプレートタグ中では、無効な値は None であると解釈されます。また、これらのテンプレートタグ中のフィルタは、無効な 値に対しも常に適用されます。
TEMPLATE_STRING_IF_INVALID にフォーマット指示文字の '%s' が 入っている場合、 '%s' は不正な変数の変数名で置き換えられます。
デバッグ専用です!
TEMPLATE_STRING_IF_INVALID はデバッグには便利ですが、「開発 時のデフォルトの設定」としてオンにするのはよい考えではありません。
Admin サイトのテンプレートをはじめとする多くのテンプレートは、実在し ない値を呼び出しても何も出力しないという動作を前提に作られています。 '' 以外の値を TEMPLATE_STRING_IF_INVALID に指定した場合、 こうしたテンプレートやサイトのレンダリングで問題が生じるかもしれません。
一般に、 TEMPLATE_STRING_IF_INVALID を有効にするのは特定のテ ンプレートに対する問題を解決したいときだけにして、デバッグが終わったら すぐに無効にしておくべきです。
皆さんはほとんどの場合、全ての値を入れた辞書を Context() に渡して Context オブジェクトのインスタンスを生成していることでしょう。しかし、 Context オブジェクトはインスタンス化した後にも、通常の辞書と同じように 値を追加したり削除したりできます:
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'
Context オブジェクトは一種のスタックです。すなわち、 push() や pop() 操作を行えます。 pop() しすぎると、 django.template.ContextPopException を送出します:
>>> c = Context() >>> c['foo'] = 'first level' >>> c.push() >>> c['foo'] = 'second level' >>> c['foo'] 'second level' >>> c.pop() >>> c['foo'] 'first level' >>> c['foo'] = 'overwritten' >>> c['foo'] 'overwritten' >>> c.pop() Traceback (most recent call last): ... django.template.ContextPopException
Context をスタック的に使うと、後述するカスタムテンプレートタグで便利な ことがあります。
Django には django.template.RequestContext という特別なコンテキストクラ スがあります。このクラスは通常の django.template.Context とは少し異なる 部分があります。まず、 RequestContext は第一引数に HttpRequest をとります。例えば:
c = RequestContext(request, { 'foo': 'bar', }
第二に、 RequestContext は TEMPLATE_CONTEXT_PROCESSORS 設定 に従っていくつかの値を自動的にコンテキストに入れます。
TEMPLATE_CONTEXT_PROCESSORS 設定は呼び出し可能オブジェクトのタプルになっ ています。個々の呼び出し可能オブジェクトは コンテキストプロセッサ と呼 ばれ、リクエストオブジェクトを引数にとって。何らかの値を要素に持った辞書を 返します。この辞書はコンテキストオブジェクトに統合されます。デフォルトでは、 TEMPLATE_CONTEXT_PROCESSORS は以下のようになっています:
("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media")
各プロセッサはタプルに列挙した順に適用されます。従って、あるプロセッサが ある変数をコンテキストに入れた後、別のプロセッサが同じ名前の変数を入れれば、 後者が前者の内容を上書きします。デフォルトのプロセッサについては後で説明し ます。
RequestContext にはオプションの第三引数 processors に追加のプロセッ サのリストを指定できます。下記の例では、 RequestContext に ip_address 変数が入ります:
def ip_address_processor(request):
return {'ip_address': request.META['REMOTE_ADDR']}
def some_view(request):
# ...
c = RequestContext(request, {
'foo': 'bar',
}, [ip_address_processor])
return t.render(c)
Note
Django の render_to_response() ショートカットを使っていて、辞書オブ ジェクトを渡してコンテキストの変数を追加している場合、テンプレートはデ フォルトで (RequestContext ではなく) Context になります。テンプ レートのレンダリングに RequestContext を使うには、 render_to_response() の 3 つめの引数に RequestContext インスタ ンスを指定します。コードは以下のようになるでしょう:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
各デフォルトプロセッサの動作を以下に示します:
このプロセッサが TEMPLATE_CONTEXT_PROCESSORS にある場合、以下の 3 つの変数が RequestContext に入ります:
user -- 現在ログインしているユーザを表す auth.User インスタン ス (クライアントがログインしていない場合には AnonymousUser インス タンス) です。
messages -- 現在ログインしているユーザ宛の (文字列の) メッセージ のリストです。舞台裏では、この変数を参照するたびに request.user.get_and_delete_messaages() を呼び出しています。 request.user.get_and_delete_messaages() は、ユーザ宛の全メッセー ジを集めて返し、 Message オブジェクトを削除します。
メッセージは user.message_set.create で設定します。
perms -- 現在ログインしているユーザが有しているパーミッションを表 す、 django.core.context_processors.PermWrapper のインスタンスで す。
このプロセッサが TEMPLATE_CONTEXT_PROCESSORS にある場合、以下の 2 つの変数が RequestContext に入ります。ただし、 DEBUG の設定が True で、リクエストの IP アドレス (request.META['REMOTE_ADDR']) が INTERNAL_IPS 設定に入っている場合だけです:
このプロセッサが TEMPLATE_CONTEXT_PROCESSORS にある場合、以下の 2 つの変数が RequestContext に入ります:
詳しくは 国際化 を参照してください。
TEMPLATE_CONTEXT_PROCESSORS にこのコンテキストプロセッサを入れる と、 RequestContext に MEDIA_URL というコンテキスト変数が入ります。 MEDIA_URL には MEDIA_URL 設定の値が入っています。
このプロセッサが TEMPLATE_CONTEXT_PROCESSORS にある場合、変数 request が RequestContext に入ります。この変数は現在の HttpRequest を表現します。このプロセッサはデフォルト では有効でないので注意してください。利用したければ有効にせねばなりません。
コンテキストプロセッサのインタフェースはとても簡単です。コンテキストプロセッ サは単なる Python の関数で、 HttpRequest オブジェクトを引数にとり、 テンプレートコンテキストに追加できるような辞書を返します。コンテキストプロ セッサは、 必ず 辞書型を返さねばなりません。
自分のコードベースに自作のコンテキストプロセッサを入れてもかまいません。 Django 側からは、自作のコンテキストプロセッサを TEMPLATE_CONTEXT_PROCESSORS 設定で指定するようにしてください。
一般的には、低水準の Template API を自分で使うことはほとんどなく、テン プレートはファイルの形でファイルシステム上に保存することになるでしょう。 テンプレートディレクトリ に指定したディレクトリにテンプレートファイルを 保存してください。
Django はテンプレートローダの設定 (「Loader 型」を参照してください) に従って、いくつものテンプレートディレクトリを検索しますが、最も基本的なテ ンプレートディレクトリ指定方法は TEMPLATE_DIRS を使う方法です。
設定ファイル中で、 TEMPLATE_DIRS 設定を使ってどのディレクトリに テンプレートが収められているかを指定してください。この値は文字列のリストか タプルで指定します。文字列にはテンプレートディレクトリへの完全なパスを指定 せねばなりません。例えば:
TEMPLATE_DIRS = (
"/home/html/templates/lawrence.com",
"/home/html/templates/default",
)
Web サーバがテンプレートディレクトリの中身を読みだせる限り、テンプレートは どこにでも置けます。拡張子はなんでもよく、 .html でも .txt でも かまいませんし、拡張子を付けなくてもかまいません。
Windows を使っている場合でも、パスは Unix 形式のスラッシュを使った表記法で 指定せねばならないので注意して下さい。
Django でファイルからテンプレートを読み出すには二通りの方法があります:
例えば get_template('story_detail.html') を呼び出した場合、前述のような TEMPLATE_DIRS の設定をしていれば、Django は以下のような順番でテ ンプレートファイルを探します:
select_template(['story_253_detail.html', 'story_detail.html']) なら、 以下の順で探します:
テンプレートが見つかった時点で Django は検索を停止します。
Tip
select_template() を使うと、極めて柔軟な「テンプレート構成機能 (templatability)」を実現できます。例えば、あるニュース記事を書いていて、 記事によってはカスタムのテンプレートを使いたいとします。 select_template(['story_%s_detail.html' % story.id, 'story_detail.html']) のようにすると、個々の記事にカスタムのテンプレートを使いながら、カスタ ムのテンプレートがない記事に対してはフォールバックのテンプレートを用意 できるというわけです。
テンプレートディレクトリを複数のサブディレクトリで構成するのは可能ですし、 お勧めでもあります。慣習的には各 Django アプリケーション毎にサブディレク トリを作成します。必要に応じてさらにサブディレクトリを作成します。
精神衛生のためにもぜひテンプレートを階層化しましょう。一つのディレクトリの ルートレベルに全てのテンプレートを保存するのは、だらしないことこの上ありま せん。
サブディレクトリ下にあるテンプレートをロードするには、以下のようにスラッシュ を使って指定します:
get_template('news/story_detail.html')
この例の get_template() は、上の TEMPLATE_DIRS と同じ設定を 使った場合、以下の順番でテンプレートをロードしようと試みます:
デフォルトでは、 Django はファイルシステムベースのテンプレートローダを使っ ていますが、 Django には他のテンプレートローダも付属しており、その他のデー タソースからテンプレートをロードできるようになっています。
ファイルシステムベース以外のテンプレートローダはデフォルトでは無効化されて いますが、 TEMPLATE_LOADERS 設定を編集して有効にできます。 TEMPLATE_LOADERS は文字列のタプルで、各文字列がテンプレートロー ダを表します。組み込みのテンプレートローダを以下に示します:
ファイルシステム上の Django アプリケーションからテンプレートをロードし ます。このローダは INSTALLED_APPS の各アプリケーションについ て templates サブディレクトリを探します。ディレクトリが存在すれば、 Django はその中からテンプレートを探します。
これはすなわち、個々のアプリケーションにテンプレートを保存しておけると いうことです。このローダを使うと、 Django アプリケーションをデフォルト のテンプレート付きで配りやすくなります。
例えば、以下のように設定します:
INSTALLED_APPS = ('myproject.polls', 'myproject.music')
get_template('foo.html') は以下のディレクトリを順番に探します:
このローダは最初に import した時に最適化処理を行います。すなわち、 INSTALLED_APPS パッケージリストの中で、 templates サブディ レクトリを持つものをキャッシュします。
Django はテンプレートローダを TEMPLATE_LOADERS 設定の順番に従っ て試してゆき、マッチするテンプレートが見つかるまで探します。
テンプレートのロードとレンダの反復を減らすため、 Django は作業を自動化する ためのショートカットとして、 django.template.loader で render_to_string() を提供しています。このショートカットは、テンプレート をロードし、レンダして、結果を文字列として返します:
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', { 'foo': 'bar' })
render_to_string ショートカットは、必須の引数として、レンダリング時にロー ドするテンプレートの名前 template_name をとります。また、オプション引 数を二つとります:
dictionary テンプレートのコンテキストとして使う値の入った辞書です。第二固定引 数として渡しても構いません。 context_instance ``Context`` またはサブクラス (例えば ``RequestContext``) のインスタ ンスです。テンプレートのコンテキストとして使われます。第三固定引数 として渡してもかまいません。
render_to_response() ショートカットも参照してくだ さい。このショートカットは、結果を HttpResponse に入れて、ビューからレ ンダ内容を直接返せるようにしています。
Note
この節は、テンプレートシステムを他のアプリケーションの出力用コンポーネ ントとして使ってみたいと考えている人のためのものです。テンプレートシス テムを Django の一部として使っている人には、この節の内容はあてはまりま せん。
通常、 Django は必要な全ての設定情報を、デフォルトの設定ファイル上の設定と 環境変数 DJANGO_SETTING_FILE に指定したファイル上の設定を合わせ たものからロードします。ただし、 Django の他の部分からテンプレートシステム を切り離して使いたい場合は、環境変数を介して設定ファイルを扱うよりも、アプ リケーション内でテンプレートシステムを初期化したいでしょうから、環境変数を 使う方法はあまり便利とはいえないでしょう。
この問題を解決するためには、 DJANGO_SETTINGS_MODULE を使わない設定方法 に書かれている手動設定の方法を使う必要があります。テンプレートシステムの必 要な部分を import して、何らかのテンプレート関連の関数を呼ぶ 前に 、指定 したい設定を引数にして django.conf.settings.configure を呼び出して下さ い。少なくとも TEMPLATE_DIRS (テンプレートローダを使いたい場合), DEFAULT_CHARSET (通常はデフォルトの utf-8 で十分です), TEMPLATE_DEBUG などを設定することになるでしょう。設定可能な変数 は、 設定ファイルのドキュメント に書かれています。関 係のある変数は TEMPLATE_ で始まっている設定です。
Aug 31, 2012