revision-up-to: | 8961 (1.0) |
---|
このドキュメントについて
このドキュメントでは、Django テンプレートシステムの文法について説明して います。技術的な観点からのテンプレートシステムの仕組みや拡張方法に関す る説明は、 Python プログラマのための Django テンプレート言語ガイド を参照してください。
Django のテンプレート言語は、釣合いの取れたパワーと簡便さを実現するように、 また HTML を扱いなれた人にとっては快適になるように設計されています。 Smarty や CheetahTemplate のようなテキストベースのテンプレート言語を経験 したことがあるなら、 Django のテンプレートはしっくりくるはずです。
設計哲学
プログラミングの知識があったり、 PHP のようなプログラムコードを直接 HTML に混ぜ込む言語を使ったことがあるなら、 Django のテンプレートシステ ムが単に HTML に Python を埋め込んだものでないことに疑問を持つでしょう。 しかし、これは設計上意図して決まっていることです。テンプレートシステム はプレゼンテーション層を表現するためのもので、プログラムロジックではな いのです。
Django のテンプレートシステムは、プログラムの構成部品に似たタグ、例えば ブールテストのための {% if %} タグやループ処理のための {% for %} タグを備えています。しかし、これらのタグは単に同じ意味の Python コードとして実行されるのではなく、テンプレートシステムは Python の式を解釈実行したりはしません。テンプレートシステムがサポートしている のは、以下で定義されているデフォルトのタグ、フィルタおよびその構文だけ です (もちろん、必要に応じてテンプレート言語を 拡張する のは可能です)。
テンプレートは単なるテキストファイルでできています。 テンプレートはあらゆる テキストベースのデータ形式 (HTML, XML, CSV など) を生成できます。
テンプレートには、テンプレートを処理する際に実際の値に置き換えられる 変数 (variable) と、テンプレート上でロジックを制御する タグ (tag) が入っています。
以下に示すのは、基本的な項目をいくつか組み込んだ最小限のテンプレートです。 各項目についてはこのドキュメントの後の部分で詳しく説明します:
{% extends "base_generic.html" %} {% block title %}{{ section.title }}{% endblock %} {% block content %} <h1>{{ section.title }}</h1> {% for story in story_list %} <h2> <a href="{{ story.get_absolute_url }}"> {{ story.headline|upper }} </a> </h2> <p>{{ story.tease|truncatewords:"100" }}</p> {% endfor %} {% endblock %}
設計哲学
なぜ (Zope における TAL のような) XML ベースのテキストベースのテンプレー トではなく、テキストベースのテンプレートを使うのでしょうか ? それは、 Django のテンプレート言語を XML/HTML テンプレート以外にも使いたいと考え ているからです。 World Online では、e-mail や JavaScript、 CSV にテンプ レートを使っています。テンプレートはテキストベースの形式なら何にでも使 えるのです。
そう、もう一つあります: 人間に XML を編集させるなんて、サディスティック でしかありません!
変数 (variable) は {{ variable }} のような形式になります。テンプレート エンジンが変数名を見つけると、変数を評価して値を置き換えたものを出力します。
変数の属性値にアクセスするには、ドット (.) 表記を使います。
舞台裏
技術的には、テンプレートシステムが dot を見つけた場合、以下の順で値の評 価を試みます:
上の例では、 {{ section.title }} は section オブジェクトの title 属性に置き換えられます。
実在しない変数を使うと、テンプレートシステムは TEMPLATE_STRING_INVALID の設定値を挿入します。デフォルトの値は '' (空文字列) です。
後述の「 組み込みのリファレンスドキュメント 」を読めば、テンプレートで使 える変数名を調べる手がかりになります。
変数を表示するために手を加えたい場合には フィルタ (filter) を使います。
フィルタは {{ name|lower }} のように書きます。この例では、変数 {{ name }} の値は lower フィルタ、すなわち文字を小文字に変換するフィルタ を通してから表示します。フィルタの適用は (|) で表します。
フィルタは「連鎖 (chain)」できます。フィルタを連鎖させると、あるフィルタか らの出力を別のフィルタに渡せます。例えば、 {{ text|escape|linebreaks }} は、テキストの内容をエスケープして、改行を <p> タグに置き換える時の慣用 句としてよく使われています。
フィルタによっては引数をとります。フィルタの引数は {{ bio|truncatewords:"30" }} のような形式で表します。この例では、変数 bio の最初の 30 語を表示します。
フィルタの引数にスペースが入っている場合は、二重引用符で囲ってください。例 えば、カンマとスペースでリストを結合するには {{ list|join:", " }} のようにします。
組み込みフィルタリファレンス では、 全ての組み込みフィルタについて説明しています。
組み込みフィルタでできることを知ってもらうために、よく使うテンプレートフィ ルタをいくつか挙げましょう:
入力が空や False の場合、引数に指定したデフォルト値を使いま す。そうでなければ、入力をそのまま使います
使用例:
{{ value|default:"nothing" }}
value が存在しなかったり空だったりすると、 "nothing" を出力 します。
入力値の長さを返します。文字列とリストいずれにも作用します。
使用例:
{{ value|length }}
value が ['a', 'b', 'c', 'd'] なら、 4 を出力します。
[X]HTML タグを全てはぎとります。
使用例:
{{ value|striptags }}
value が "<b>Joel</b> <button>is</button> a <span>slug</span>" なら、 "Joel is a slug" を出力します。
これらはほんの一部に過ぎません。全ての組み込みフィルタの一覧は 組み込みフィルタリファレンス を参照 してください。
テンプレートフィルタは自作できます。 テンプレートタグやフィルタを自作する を参照してください。
タグは {% tag %} のように書きます。タグの考え方は、変数よりもやや複雑 です。出力されるテキストを生成するものもありますし、ループやロジックを実行 して、処理の流れを制御するものもあります。また、外部の情報を取り込んで、後 に出て来る変数から使えるようにするものもあります。
タグによっては、開始タグと終了タグ ({% tag %} ... tag contents ... {% endtag %}) を必要とするものがありま す。
Django にはたくさんの組み込みタグが付属しています。組み込みタグの解説は 組み込みタグリファレンス にあります。 組み込みタグでできることを知ってもらうために、よく使うタグをいくつか挙げま しょう:
アレイの各要素に渡ってループします。例えば、アスリート (athlete) の リストを athlete_list で渡して表示するには、以下のようにします:
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
変数を評価して、値が「真」 (値が存在して、空の配列でなく、ブール値 が偽でない) の場合、ブロック内のコンテンツを出力します:
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% else %} No athletes. {% endif %}
上の例では、 athlete_list が空でなければ、アスリートの人数を {{ athlete_list|length }} で表示します。
2 つの引数が互いに等しい場合にブロックの内容を出力します。例えば:
{% ifequal athlete.name coach.name %} ... {% endifequal %}
あるいは、以下のように書きます:
{% ifnotequal athlete.name "Joe" %} ... {% endifequal %}
これらはほんの一部に過ぎません。全ての組み込みフィルタの一覧は 組み込みタグリファレンス を参照してく ださい。
テンプレートタグは自作できます。 テンプレートタグやフィルタを自作する を参照してください。
テンプレート中で行内の一部をコメントアウトするには、コメント構文 {# #} を使います。
例えば、以下のテンプレートをレンダすると 'hello' になります:
{# greeting #}hello
コメント内には任意のテンプレートコードを入れられます。コメント内の テンプレートコードが無効なものであってもエラーにはなりません:
{# {% if foo %}bar{% else %} #}
この構文を使えるのは、一行のコメントの場合だけです ({# と #} の間に は改行を入れられません)。複数行のコメントアウトを行いたければ、後述の comment タグを使ってください。
Django のテンプレートエンジンにおいて、テンプレートの継承 (inheritance) は 最もパワフル -- かつ最も難解 -- な部分です。テンプレートの継承は、自分のサ イトで共通に使われる全ての要素の入った、ベースとなる「骨組みの」テンプレー トを作っておき、その中に子のテンプレートでオーバライドできる ブロック(block) を定義できます。
テンプレートの継承を理解するには、まず例を挙げるのが一番でしょう:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
このテンプレートは、単純な 2 カラム形式のページで使うような HTML のスケルト ンドキュメントです。これを base.html と呼びましょう。空のブロックをコン テンツで埋めるのは「子 (child)」のテンプレートの仕事です。
この例では、 {% block %} タグを使って 3 つのブロックを定義し、子テンプ レートが値を埋められるようにしています。 block タグの役割は、テンプレー ト中のタグで囲まれた部分を子テンプレートでオーバライドできることをテンプレー トエンジンに知らせることにあります。
子テンプレートは以下のようになります:
{% extends "base.html" %} {% block title %}My amazing blog{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %}
{% extends %} タグが継承のカギです。このタグはテンプレートエンジンに対 して、自分自身が他のテンプレートを拡張 (extend) していることを教えます。テ ンプレートシステムがこのテンプレートを処理する際、システムはまず親となるテ ンプレート -- ここでは "base.html" を探します。
さて、この時点で、テンプレートエンジンは base.html 内に三箇所の {% block %} が定義されていることに気づき、これらのブロックを子テンプレー トの該当するブロックで置き換えます。 blog_entries の値に応じて、出力は 以下のようになります:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>My amazing blog</title> </head> <body> <div id="sidebar"> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </div> <div id="content"> <h2>Entry one</h2> <p>This is my first entry.</p> <h2>Entry two</h2> <p>This is my second entry.</p> </div> </body> </html>
子テンプレートには sidebar ブロックが定義されていないので、親テンプレー トの値がそのまま使われます。親テンプレートの {% block %} タグの内容は、 常にフォールバックの値として使われます。
テンプレートの継承は必要に応じて何段階にもできます。継承を使うよくある場合 の一つに、以下のような三段階のアプローチがあります:
このようなアプローチを取れば、コードの最利用性を最大限に高め、セクション毎 のナビゲーションのような項目を簡単に共通のコンテンツ領域に追加できます。
継承を扱うときの小技をいくつか挙げておきます:
テンプレートで {% extends %} を使う場合は、テンプレート中の最初の テンプレートタグにせねばなりません。さもなければテンプレート継承はう まく動作しません。
ベースのテンプレートで {% block %} を多用すればするほど、よりよい テンプレートになります。子テンプレートは親テンプレートのブロックを必 ずしも全て定義しなくてもよいことを思い出して下さい。ブロックをたくさ ん用意しておき、適切なデフォルト値を入れておいて、子テンプレートで必 要な値だけを再定義すればよいのです。フックは少ないよりも沢山ある方が よいのです。
同じような内容を含むテンプレートをいくつも作っていることに気づいたら、 それはすなわちその内容を親テンプレートの {% block %} に入れるべき だということです。
親テンプレートのブロックに入っているコンテンツを取り出す必要がある場 合、 {{ block.super }} とするとうまくいきます。親テンプレートのブ ロックをオーバライドするのではなく、内容を追加したい場合に便利です。 {{ block.super }} で挿入されたデータは、通常、親テンプレートで 既にエスケープされているので、自動的にエスケープされません (HTML の自動エスケープ を参照)。
可読性を高めるために、例えば以下のように、 {% endblock %} にブロッ クの 名前 を指定できます:
{% block content %} ... {% endblock content %}
大きなテンプレートの編集で、どこで {% block %} タグが閉じているか 探すのに便利です。
最後に、同じテンプレート中に同じ名前の {% block %} を複数定義できないこ とに注意して下さい。この制限は、ブロックタグが「双方向」に作用するため、す なわち、あるブロックタグは何らかの値を埋めるための穴であるだけでなく、穴を 埋める 親テンプレートの コンテンツも定義しているために存在しています。同 じ名前の {% block %} が一つのテンプレート内に複数あると、そのテンプレー トの親テンプレートは、該当するブロックの内容を子テンプレートのどの {% block %} 設定で置き換えればよいのか分からなくなってしまいます。
テンプレートから HTML を生成するときには、変数内の文字が HTML のレンダリン グ結果を壊してしまうというリスクが常に付きまといます。例えば、以下のような テンプレートフラグメントを考えてみましょう:
こんにちは {{ name }} さん
一件、このコードはユーザ名を表示するだけの無害なものに見えますが、名前を以 下のような値にすると、何が起こるでしょう:
<script>alert('hello')</script>
テンプレートは以下のようにレンダされます:
Hello, <script>alert('hello')</script>
その結果、 JavaScript の警告ボックスを表示できてしまいます!
同様に、名前に以下のようにして '<' 文字をいれると同でしょう:
<b>username
レンダ結果は以下のようになります:
Hello, <b>username
これで、以降のWebページは全部ボールド表示になってしまいます!
言うまでもなく、ユーザが入力したデータは盲目に信頼してはならず、直接 Web ペー ジに挿入すべきでもありません。悪意のあるユーザはこの手の穴を使って悪さをす るものです。こうしたセキュリティホールは、 クロスサイトスクリプティング (XSS) 攻撃と呼ばれています。
クロスサイトスクリプティングを防ぐには、二つの方法があります:
開発版の Django では、デフォルトの設定として、全てのテンプレートは変数タグ を自動的にエスケープします。具体的には、以下の 5 つの文字がエスケープされます:
この動作はデフォルトで適用されていることを強調しておきます。Django テンプレー トシステムを使っているかぎり、エスケープに関する問題からは守られているのです。
サイト単位やテンプレート単位、変数単位でデータの自動エスケープを切るには、 いくつかの方法があります。
どんなときに、自動エスケープを切る必要があるのでしょうか。テンプレート変数 の中には、生の HTML としてレンダするように 意図された データがあります。 そうした場合にはコンテンツがエスケープされてほしくはないでしょう。例えば、 データベースに HTML を保存していて、テンプレートに直接埋め込みたい場合を考 えてみましょう。また、 Django のテンプレートシステムを使って、 HTML 以外 のデータ、例えば電子メールメッセージなどを生成したい場合がそうです。
変数個々に自動エスケープを無効にするには、 safe フィルタを使います:
エスケープされる: {{ data }} エスケープされない: {{ data|safe }}
safe という言葉を、 これ以上エスケープしないよう保護(safe)する とか、 HTML として解釈しても安全(safe)である という意味だと考えてください。 例えば、 data に '<b>' が入っていた場合、出力は以下のようになります:
エスケープされる: <b> エスケープされない: <b>
テンプレートで自動エスケープを制御するには、テンプレート (または テンプレートの一部) を、以下のように autoescape タグで囲みます:
{% autoescape off %} こんにちは {{ name }} さん {% endautoescape %}
autoescape タグは、 on または off を引数にとります。 テンプレートのある範囲を自動エスケープして、さらにその一部で自動エスケープ を切りたい場合には、以下のように入れ子にできます:
Auto-escaping is on by default. Hello {{ name }} {% autoescape off %} This will not be auto-escaped: {{ data }}. Nor this: {{ other_data }} {% autoescape on %} Auto-escaping applies again: {{ name }} {% endautoescape %} {% endautoescape %}
自動エスケープのタグは、他のブロックタグと同様、タグを設定したテンプレート を継承している他のテンプレートや、 include で取り込んだテンプレートでも 有効です。例えば:
# base.html {% autoescape off %} <h1>{% block title %}{% endblock %}</h1> {% block content %} {% endblock %} {% endautoescape %} # child.html {% extends "base.html" %} {% block title %}This & that{% endblock %} {% block content %}{{ greeting }}{% endblock %}
自動エスケープがベースのテンプレートで無効化されているので、子テンプレート でも自動エスケープは無効化されます。結果として、 greeting 変数の値が <b>Hello!</b> の時には以下の HTML がレンダされます:
<h1>This & that</h1> <b>Hello!</b>
一般に、テンプレートの作者は自動エスケープをあまり意識するべきではありませ ん。 Python サイドの開発者 (ビューやカスタムフィルタ) の作者が、どのデータ をエスケープすべきでないかを考え、データを適切にマークして、テンプレートに うまく表示されるようにすべきなのです。
テンプレートを作成していて、自動エスケープが有効な環境で使われるかどうか分 からないような場合には、エスケープの必要な変数全てに escape フィルタを 追加してください。自動エスケープがオンの場合、 escape フィルタがデータ を 二重にエスケープする ような危険性はありません。 escape フィルタは 変数の自動エスケープには影響しないのです。
先に説明したように、フィルタの引数は文字列であってもかまいません:
{{ data|default:"文字列リテラルだよ" }}
文字列リテラルは、自動エスケープ されずに テンプレート内に挿入されます。 文字列リテラルは、 safe フィルタを通して渡されたかのように振る舞います。 このような仕様になっているのは、テンプレートの作者が文字列リテラルがどのよ うに処理されるかを制御でき、テンプレートを書くときにテキストが正しくエスケー プされるよう配慮できるからです。
従って、以下のようにテンプレートを書いてください:
{{ data|default:"3 < 2" }}
以下のようにはしないでください:
{{ data|default:"3 < 2" }} <-- ダメ。こんな書き方をしてはいけません
引数にリテラルを使っても、変数自体に対するエスケープの挙動には影響しません。 変数はテンプレート作者の制御の範囲外にあり、自動的にエスケープされます。
Django の admin インタフェースには、サイトで使える全てのテンプレート関連要 素のリファレンスがあります。このドキュメントを読みたければ、 admin インタ フェースにアクセスして、ページの右上部分にある「ドキュメント (Documentation)」というリンクをクリックして下さい。
リファレンスは、タグ(Tags)、フィルタ(Filters)、モデル(Models)、そしてビュー (Views)の 4 つのセクションに分かれています。
Tags や Filters セクションでは、全ての組み込みタグについて解説して います。 (実際、後で示すタグやフィルタのリファレンスは全てこれらのセクショ ンから直接取り出したものです) また、利用可能なカスタムのタグやフィルタライ ブラリについてのリファレンスもあります。
最も大事なのは Views ページです。サイトの各 URL には個別のエントリが 表示され、 URL をクリックすると以下のような内容を表示します:
各ビューのドキュメントにはブックマークレット(bookmarklet)があり、別のページ からこのビューのドキュメントにジャンプするために使えます。
Django で作成したサイトは通常、データベースオブジェクト中心に動いているので、 Models のセクションはシステム中の各タイプのオブジェクトと、そのオブジェ クトで利用できる全てのフィールドについて説明しています。
ドキュメントのページを読めば、全てのタグ、フィルタ、テンプレートで利用でき る変数やオブジェクトが分かります。
アプリケーションによっては、カスタムのタグやフィルタライブラリを提供してい ます。こうしたタグをテンプレートで使いたい場合、 {% load %} タグを使い ます:
{% load comments %} {% comment_form for blogs.entries entry.id with is_public yes %}
上の例では、 load タグは comments という名前のタグライブラリを読み 込んでいます。このタグライブラリを読み込むと、 comment_form タグを使え るようになります。カスタムライブラリのリストを探したければ、自分のサイトの admin でドキュメント置場を参照してください。
{% load %} タグは複数のライブラリ名を同時に読み込めます。ライブラリ名は スペースで区切って下さい:
{% load comments i18n %}
カスタムのテンプレートライブラリを作成する方法は テンプレートタグやフィルタを自作する を参照してください。
カスタムタグやフィルタライブラリをロードした場合、タグやフィルタは現在のテ ンプレートだけで利用でき、テンプレート継承パス上の親テンプレートや子テンプ レートでは利用できません。
例えば、 foo.html に {% load comments %} というタグを入れておいても、 子テンプレート ({% extends "foo.html" %} を使っているテンプレート) で comments という名前のテンプレートタグやフィルタを使えるわけでは ありません 。子テンプレート側で独自に {% load comments %} を入れてお く必要があります。
これはメンテナンス性を高めるための仕様です。
Aug 31, 2012