Django テンプレート言語¶
このドキュメントはDjangoテンプレートシステムの文法を説明しています。もしあなたがもっと技術者的な観点でどう動かすかやどう拡張するかを知りたい場合は、Python プログラマーのための Django テンプレート言語 を参照してください。
Django のテンプレート言語は、強力さと使いやすさのバランスを取るように設計されています。HTML の扱いに慣れている人なら、違和感なく使えるように設計されています。 Smarty や Jinja2 のようなテキストベースのテンプレート言語に触れたことがあれば、 Django のテンプレートに馴染めるはずです。
設計思想
もしあなたがプログラミングのバックグラウンドを持っていたり、 HTML に直接プログラミングコードを埋め込む言語に慣れているなら、 Django のテンプレートシステムは単に Python を HTML に埋め込んだものではないことを心に留めておいてください。これは、テンプレートシステムはプレゼンテーション(体裁)を表現するためのものであり、プログラムロジックを表現するためのものではないという設計思想から来ています。
Django テンプレートシステムは、いくつかのプログラミング構文に似た機能を持つタグを提供します。真偽値テスト用の if
タグ、ループ処理用の for
タグなどです。しかし、これらは単純に対応する Python コードとして実行されるわけではなく、テンプレートシステムは任意の Python 式を実行することはありません。デフォルトでは以下のタグ、フィルタ、構文だけがサポートされています(ただし、必要に応じて 独自の拡張機能 をテンプレート言語に追加することもできます)。
テンプレート¶
テンプレートは、テキストファイルです。これは、任意のテキストベースのフォーマット (HTML、XML、CSV) などを生成できます。
テンプレートに含まれるのは、テンプレートが読み込まれたとき実際の値で置き換えられる 変数 と、テンプレートのロジックを制御する タグ です。
以下は、いくつかの基本を示した最小限のテンプレートです。各要素については、この文書の後半で説明します。
{% 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 テンプレートより使いやすくしたかったからです。テンプレート言語は、どんなEメール、JavaScript、CSVのようなテキストベースのフォーマットにも使用できます。
変数¶
変数は次のような見た目です: {{ variable }}
。テンプレートエンジンが変数を検出すると、その変数を認識し、その結果に置き換えます。変数名は、英数字の任意の組み合わせとアンダースコア ("_"
) で構成されています(ただし、先頭を2重のアンダースコアもしくは数字にすることはできません)。ドット ("."
) も変数セクションに表示されますが、以下に示すように特別な意味を持っています。重要なのは、変数名にスペースまたは句読点を持つことはできない ということです。
変数の属性にアクセスするには、ドットを ('.
') を使用します。
背景
技術的には、テンプレートシステムがドットを検出した際、以下の参照項目をこの順番で実行しようとします:
- 辞書検索
- 属性やメソッドの検索
- 数値のインデックス検索
結果の値が呼び出し可能である場合は、引数なしで呼び出されます。呼び出しの結果は、テンプレートの値となります。
この検索の手順は、辞書検索を上書きするオブジェクトにおいて、予期しない動作を引き起こす可能性があります。たとえば、collections.defaultdict
すべてをループ処理をしようとする、次のコードスニペットを考えてみましょう。
{% for k, v in defaultdict.items %}
Do something with k and v here...
{% endfor %}
最初に辞書検索が行われるため、その挙動が動作してしまい、意図していた .items()
メソッドを使用せずに、デフォルト値を提供してしまいます。この場合、最初に辞書を変換することを検討してください。
上記の例では、 {{ section.title }}
は section
オブジェクトの title
属性と置き換えられます。
もし存在しない変数を使いたい場合、テンプレートシステムはデフォルトで ''
(the empty string)が置かれている場所に、 string_if_invalid
のオプション値を挿入します。
テンプレートコンテキストに変数 "bar "が存在する場合、{{ foo.bar }}
のようなテンプレート式中の "bar "はリテラル文字列として解釈され、変数 "bar" の値は使用されないことに注意してください。
アンダースコアで始まる変数属性は、通常はプライベートとみなされるため、アクセスできません。
フィルタ¶
フィルタ を使って、表示される変数に手を加えることができます。
フィルタは {{ name|lower }}
のような形式です。これは {{ name }}
変数の値を、テキストを小文字に変換する lower
フィルタを通してから表示します。フィルタを適用するにはパイプ (|
) を使用します。
フィルタは "連結" できます。あるフィルタの出力が次のフィルタに適用されます。{{ text|escape|linebreaks }}
はテキストの内容をエスケープし、改行を <p>
タグに変換するためのよくあるイディオムです。
いくつかのフィルタは引数を取ります。フィルタの引数は {{ bio|truncatewords:30 }}
のように指定します。これは bio
変数の最初の30単語を表示します。
スペースを含むフィルタの引数は引用符で囲む必要があります。たとえば、カンマとスペースを含むリストを結合するには {{ list|join:", " }}
とします。
Django には約 60 個の組み込みテンプレートフィルタがあります。これらについては 組み込みフィルタリファレンス を参照してください。どのようなものがあるのかを知ってもらうために、よく使われるテンプレートフィルタをいくつか紹介します:
default
変数が False または空の場合、指定されたデフォルト値を使用します。そうでなければ、その変数の値を使います。たとえば:
{{ value|default:"nothing" }}
もし
value
が与えられなかったり、空だった場合、上記は "nothing
" と表示されます。length
値の長さを返します。これは文字列でもリストでも使えます。たとえば:
{{ value|length }}
value
が['a', 'b', 'c', 'd']
のとき、出力は4
となります。filesizeformat
入力値を、"人間が読みやすい" ファイルサイズ表現 ('13 KB', '4.1 MB', '102 bytes' など) に変換します。たとえば:
{{ value|filesizeformat }}
value
が 123456789 のとき、出力は117.7 MB
になります。
繰り返しますが、これらはほんの一例に過ぎません。完全なリストは 組み込みフィルタのリファレンス を参照してください。
独自のカスタムテンプレートフィルタを作成することもできます。 独自のテンプレートタグとフィルタを作る を参照してください。
参考
Django の管理インタフェースには、指定したサイトで利用可能な全てのテンプレートタグとフィルタの完全なリファレンスを含めることができます。 Django admin ドキュメントジェネレータ を参照してください。
タグ¶
タグは {% tag %}
のような形式です。タグは変数よりも複雑です。出力にテキストを生成したり、ループやロジックを実行することでフローを制御したり、後の変数で使用される追加情報をテンプレートにロードしたりします。
タグの中には開始タグと終了タグを必要とするものがあります(例 {% tag %} ... tag contents ... {% endtag %}
)。
Django には約 20 個の組み込みテンプレートタグが同梱されています。これらについては 組み込みタグのリファレンス を参照してください。どのようなタグが利用できるかを知ってもらうために、よく使われるタグをいくつか紹介 します:
for
配列の各要素に渡ってループします。たとえば、アスリート (athlete) のリストを
athlete_list
で渡して表示するには、次のようにします。<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
if
,elif
,else
変数を評価し、その変数が "true "の場合、ブロックの内容が表示されます:
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% elif athlete_in_locker_room_list %} Athletes should be out of the locker room soon! {% else %} No athletes. {% endif %}
上記の場合、
athlete_list
が空でなければ、{{ athlete_list|length }}
変数によって選手の数が表示されます。もしathlete_in_locker_room_list
が空でなければ、"Athletes should be out... " というメッセージが表示されます。両方のリストが空の場合、"No athletes." と表示されます。if
タグの中では、フィルタや様々な演算子を使うこともできます:{% if athlete_list|length > 1 %} Team: {% for athlete in athlete_list %} ... {% endfor %} {% else %} Athlete: {{ athlete_list.0.name }} {% endif %}
上の例はうまくいきますが、ほとんどのテンプレートフィルタは文字列を返すので、フィルタを使った数学的な比較は通常は期待通りにならないことに注意してください。
length
は例外です。block
とextends
- テンプレートの "定型文" を削減する強力な方法である template inheritance (下記参照)を設定します。
繰り返しますが、上記は全リストの一部です。全リストは 組み込みタグのリファレンス を参照してください。
独自のカスタムテンプレートタグを作成することもできます。 独自のテンプレートタグとフィルタを作る を参照してください。
参考
Django の管理インタフェースには、指定したサイトで利用可能な全てのテンプレートタグとフィルタの完全なリファレンスを含めることができます。 Django admin ドキュメントジェネレータ を参照してください。
コメント¶
テンプレートの行の一部をコメントアウトするには、コメント構文 {# #}
を使います。
たとえば、このテンプレートは 'hello'
としてレンダリングされます:
{# greeting #}hello
コメントには、たとえ無効でも、どんなテンプレート・コードも含めることができます。たとえば:
{# {% if foo %}bar{% else %} #}
この構文は1行のコメントにしか使えません(改行は {#
と #}
の間に入れてはいけません)。テンプレートの複数行の部分をコメントアウトする必要がある場合は comment
タグを参照してください。
テンプレートの継承¶
Django のテンプレートエンジンの中で最もパワフルな -- ゆえに最も複雑な -- 部分が、テンプレートの継承です。テンプレートを継承すると、ベースとなる "骨組みの" テンプレートにサイトの共通要素を記述し、block を定義することで子テンプレートにオーバーライドさせることができます。
テンプレートの継承について、まず例を見てみましょう:
<!DOCTYPE html>
<html 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>
我々が base.html
と呼ぶことになるこのテンプレートは、HTML の骨組みを定義し、2 列のサイトを表示できるようになっています。block をコンテンツで埋めるのは、"子" テンプレートの仕事です。
この例では、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
タグです。このタグが、このテンプレートが他のテンプレートを "拡張" することをテンプレートエンジンに伝えるのです。テンプレートシステムがこのテンプレートを評価する際に、最初に親テンプレートを探しに行きます -- この例では "base.html" です。
この時点で、テンプレートエンジンは base.html
内の block
を認識し、子テンプレートのコンテンツと差し替えます。blog_entries
の値に応じて、アウトプットは以下のようになります:
<!DOCTYPE html>
<html 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 %}
タグ内のコンテンツは、常にフォールバックとして使用されます。
継承は、必要なだけ段階を経ることができます。継承を使う一般的な方法は、以下の 3 段階のアプローチです:
- メインのルック・アンド・フィールを保持する
base.html
を作成します。 - サイトのそれぞれの「セクション」に対して、
base_セクション名.html
テンプレートを作成します。たとえば、base_news.html
、base_sports.html
などです。これらのテンプレートはすべてbase.html
を拡張 (extend) し、セクション固有のスタイルやデザインが含まれています。 - ページのそれぞれのタイプに対して、個別のテンプレートを作成します。たとえばニュース記事やブログエントリーなどです。これらのテンプレートは、適切なセクションの手プレートを拡張 (extends) します。
このアプローチにより、コードを最大限に再利用でき、共通のコンテンツ部 (全セクション共通のナビゲーションなど) に項目を追加するのに役立ちます。
継承を使用する際のコツをいくつか紹介します:
テンプレート内で
{% extends %}
を使用する際は、その点プレート内で一番最初のテンプレートタグとして指定する必要があります。それ以外の場合、継承が正しく動作しません。ベースのテンプレートには
{% block %}
タグを多く配置するほどよいです。子テンプレートがすべてのブロックを埋める必要がないことを思い出してください。多くのブロック内に適当なデフォルトを記述しておき、必要なものを後で定義し直せばいいのです。フックは多ければ多いほどいいということです。もし同じコンテンツを複数のテンプレート内で記述していたら、そのコンテンツを親テンプレートの
{% block %}
に移動させるべきかもしれません。親テンプレートからブロックのコンテンツを取得する必要がある場合、
{{ block.super }}
が利用できます。親のブロックを完全にオーバーライドせずに、コンテンツを追加したい場合に有用です。{{ block.super }}
を使って挿入されたデータは自動的にエスケープされません (next section を参照してください)。これは、必要な場合は親テンプレートですでにエスケープされているからです。継承元と同じテンプレート名を使うことで、
{% extends %}
を使ってテンプレートのオーバーライドと同時に継承できます。{{ block.super }}
と組み合わせることで、カスタマイズを最小限に留める強力な手法となります。完全な例は テンプレートのオーバーライド How-to の オーバーライドされたテンプレートを継承(extend)する を参照してください。{% block %}
の外側でテンプレートタグのas
構文を使って作成した変数はブロックの中では使用できません。たとえば、このテンプレートは何もレンダリングしません:{% translate "Title" as title %} {% block content %}{{ title }}{% endblock %}
さらに読みやすくするために、オプションで
{% endblock %}
タグに 名前 をつけることができます。たとえば:{% block content %} ... {% endblock content %}
大きなテンプレートでは、このテクニックでどの
{% block %}
が閉じられているのかが分かりやすくなります。{% block %}
タグが最初に評価されます。そのため、周囲のタグの真偽に関わらず、ブロックの内容は常に上書きされます。たとえば、このテンプレートはtitle
ブロックの内容を常に上書きします:{% if change_title %} {% block title %}Hello!{% endblock title %} {% endif %}
最後に、同じテンプレート内で同じ名前の block
タグを複数定義することはできないことに留意してください。この制限は、block タグが "両方向に" 働くのが理由です。block タグは埋めるための穴を提供するだけではなく -- 親テンプレート 内で穴を埋めるコンテンツを定義します。もし同名の block
がテンプレート内に 2 つ存在すると、どちらのブロックのコンテンツを使うべきかテンプレートの親が判断できなくなってしまうのです。
HTML の自動エスケープ¶
テンプレートから HTML を生成する際は、変数内の文字が HTML のレンダリング結果に悪影響を与えるというリスクが常に付きまといます。たとえば、テンプレートに以下のような部分がある場合を考えてみましょう:
Hello, {{ name }}
一見、このコードはユーザの名前を表示するだけの無害なものに見えます。しかし次のような値が名前として入力されていれば、何が起きるでしょうか:
<script>alert('hello')</script>
この値によって、テンプレートは次のように出力されます:
Hello, <script>alert('hello')</script>
……結果として、ブラウザに JavaScript の警告ボックスをポップアップさせることができてしまいます!
同様に、名前に '<'
文字が含まれていたらどうでしょうか:
<b>username
この値によって、テンプレートはこのように出力されます:
Hello, <b>username
これで、以降の Web ページの内容はすべて太字になってしまいます!
ユーザーが入力したデータを無条件に信頼したり、直接 Web ページに挿入したりするべきでないことは明らかです。悪意のあるユーザは、この手の抜け穴を使って悪さをするものだからです。こうしたタイプのセキュリティホールの悪用は、 Cross Site Scripting (XSS) 攻撃と呼ばれています。
この問題を防ぐために、二つの方法があります:
- 一つは、信頼できない変数は必ず
escape
フィルタ (後で述べます) に通すことです。このフィルタは危害を及ぼす可能性をもつ HTML 文字を無害な文字に変換します。 この方法は初期の数年間において Django のデフォルトとして採用されていました。しかしユーザー、すなわち開発者やテンプレートの作者にとっては、すべてのデータをエスケープすることを忘れぬよう注意しなければならないという負担になるのが問題です。 - もう一つは、 Django の自動 HTML エスケープを使うというものです。この節の後半では自動エスケープの仕組みについて述べます。
デフォルトでは、Django はすべてのテンプレートの変数タグの出力を自動的にエスケープするよう設定されています。具体的には、以下の 5 つの文字がエスケープされます:
<
を<
に変換>
を>
に変換'
( シングルクォート ) を'
に変換"
( ダブルクォート ) を"
に変換&
を&
に変換
この動作は、デフォルトで適用されていることを重ねて強調しておきます。Django テンプレートシステムを使っているかぎり、あなたはエスケープに関する問題からは守られているのです。
自動エスケープをオフにする¶
サイト単位やテンプレート単位、あるいは変数単位でデータの自動エスケープ機能を無効にしたい場合には、いくつかの方法があります。
どんなときに、自動エスケープをオフにする必要があるでしょうか。テンプレート変数の中身は、生の HTML として出力するように意図されたデータかもしれません。そうした場合にはコンテンツがエスケープされてほしくはないでしょう。たとえば、データベースに HTML を保存していてテンプレートに直接埋め込みたい場合を考えてみましょう。また、 Django のテンプレートシステムを使って、 HTML 以外 のデータ、たとえばメールのメッセージなどを生成したい場合もあるでしょう。
変数単位での制御¶
個々の変数の自動エスケープを無効にするには safe
フィルタを使用します:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
safe という言葉は、これ以上エスケープしないよう保護 (safe) するとか、 HTML として解釈しても安全 (safe) であるという意味だと考えてください。 たとえば、data
に '<b>'
が入っていた場合、出力は以下のようになります。
This will be escaped: <b>
This will not be escaped: <b>
テンプレートブロック単位での制御¶
テンプレート単位で自動エスケープを制御するには、テンプレート (または テンプレートの一部) を、以下のように autoescape
タグで囲みます:
{% autoescape off %}
Hello {{ 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:"This is a string literal." }}
文字列リテラルは、すべて自動エスケープ されずに テンプレート内に挿入され、 safe
フィルタを通過したかのように動作します。なぜなら、テンプレートの作者はその文字列リテラルに何が書かれているかは把握していますし、テンプレートが書かれる時点でテキストが正しくエスケープされていることは確認できるからです。
したがって、以下のようにテンプレートを書いてください:
{{ data|default:"3 < 2" }}
以下のようにはしないでください:
{{ data|default:"3 < 2" }} {# Bad! Don't do this. #}
この事が、変数自身に由来するデータに対して何かを引き起こす恐れはありません。 変数の内容はテンプレート作者の管理外にあり、必要に応じて自動的にエスケープされます。
メソッドへのアクセス¶
オブジェクトに備わったほとんどのメソッドは、テンプレートの中からでも呼び出すことができます。これはテンプレートからアクセスできる対象は、単にクラスの属性 (フィールド名のように) やビューを通した変数だけにはとどまらないということを意味します。たとえば Django ORM は外部キーに関係するオブジェクトの集合を見つけるために "entry_set" という構文を提供しています。これによって、たとえば "comment" と呼ばれるモデルが外部キーで "task" というモデルに関連づけられている場合、テンプレートは task モデルに添えられたすべての comment に対して、以下のようにアクセスできます。
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
同じように、 QuerySet は、そこに含まれているオブジェクトの個数を数えるため count()
というメソッドを提供しています。これによって、 task と関連づけられた全てのコメントの数を取得することも出来ます:
{{ task.comment_set.all.count }}
モデル自身で明示的に定義したメソッドにもアクセスできます:
models.py
¶class Task(models.Model):
def foo(self):
return "bar"
template.html
¶{{ task.foo }}
Django ではテンプレート内でのプログラム的な処理を意図的に制限しているため、テンプレートからメソッドに引数を渡すことはできません。データはビューの中で計算され、テンプレートへは表示のためだけに渡されるべきだからです。
カスタムタグとカスタムフィルタのライブラリ¶
アプリケーションによっては、カスタムのタグやフィルタライブラリを提供しています。これらをテンプレートで使いたい場合、そのアプリケーションが INSTALLED_APPS
に追加されていることを確認し (以下の例では 'django.contrib.humanize'
が追加されているものとします) 、そしてテンプレート側で load
タグを使います:
{% load humanize %}
{{ 45000|intcomma }}
この例では、 load
タグは humanize
という名前のタグライブラリを読み込み、 intcomma
フィルタを使えるようにしています。 django.contrib.admindocs
が有効にされている場合、あなたの admin は現在インストールされているアプリケーションのドキュメント文字列を参照してカスタムライブラリの一覧を得ることができます。
load
タグは複数のライブラリ名を同時に読み込めます。ライブラリ名はスペースで区切って下さい:
{% load humanize i18n %}
カスタムのテンプレートライブラリを作成する方法については、 独自のテンプレートタグとフィルタを作る を参照してください。
カスタムライブラリとテンプレートの継承¶
カスタムタグやフィルタライブラリをロードした場合、タグやフィルタは現在のテンプレートだけで利用でき、テンプレート継承パスにおける親テンプレートや子テンプレートでは利用できません。
たとえば foo.html
に {% load humanize %}
というタグが入っていても、子テンプレート ( {% extends "foo.html" %}
を使っているテンプレート) で humanize という名前のテンプレートタグやフィルタを使えるわけではありません。子テンプレート側で対応するには独自に {% load humanize %}
を入れる必要があります。
これはメンテナンス性と健全性を高めるための仕様です。
参考
- テンプレートのリファレンス
- 組み込みタグ、組み込みフィルタ、代替のテンプレート言語など多くをカバーしています。