Djangoテンプレート言語
このドキュメントでは、Djangoテンプレートシステムの言語構文について説明します。 それがどのように機能し、どのように拡張するかについて、より技術的な視点を探している場合は、 Djangoテンプレート言語:Pythonプログラマー向けを参照してください。
Djangoのテンプレート言語は、パワーと使いやすさのバランスをとるように設計されています。 HTMLの操作に慣れている人が快適に過ごせるように設計されています。 Smarty や Jinja2 など、他のテキストベースのテンプレート言語に触れている場合は、Djangoのテンプレートに慣れているはずです。
哲学
プログラミングのバックグラウンドがある場合、またはプログラミングコードを直接HTMLに混合する言語に慣れている場合は、Djangoテンプレートシステムが単にHTMLに埋め込まれたPythonではないことに注意してください。 これは仕様によるものです。テンプレートシステムは、プログラムロジックではなく、プレゼンテーションを表現することを目的としています。
Djangoテンプレートシステムは、いくつかのプログラミング構造と同様に機能するタグを提供します–ブールテスト用の:ttag: `if` タグ、ループ用の:ttag:` 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 %}
哲学
XMLベースのテンプレート(ZopeのTALなど)の代わりにテキストベースのテンプレートを使用するのはなぜですか? Djangoのテンプレート言語をXML / HTMLテンプレート以外にも使用できるようにしたかったのです。 World Onlineでは、メール、JavaScript、CSVに使用しています。 テンプレート言語は、任意のテキストベースの形式に使用できます。
ああ、そしてもう1つ、人間にXMLを編集させるのはサディスティックです!
変数
変数は次のようになります:テンプレート:Variable
。 テンプレートエンジンは変数を検出すると、その変数を評価し、結果に置き換えます。 変数名は、英数字とアンダースコア("_"
)の任意の組み合わせで構成されますが、アンダースコアで始めることはできません。 ドット("."
)も可変セクションに表示されますが、以下に示すように、特別な意味があります。 重要なのは、変数名にスペースや句読文字を含めることはできないということです。
ドット(.
)を使用して、変数の属性にアクセスします。
舞台裏
技術的には、テンプレートシステムがドットを検出すると、次のルックアップをこの順序で試行します。
- 辞書検索
- 属性またはメソッドのルックアップ
- 数値インデックスルックアップ
結果の値が呼び出し可能である場合、引数なしで呼び出されます。 呼び出しの結果がテンプレート値になります。
このルックアップ順序は、ディクショナリルックアップをオーバーライドするオブジェクトで予期しない動作を引き起こす可能性があります。 たとえば、collections.defaultdict
をループしようとする次のコードスニペットについて考えてみます。
{% for k, v in defaultdict.items %}
Do something with k and v here...
{% endfor %}
ディクショナリルックアップが最初に行われるため、目的の.items()
メソッドを使用する代わりに、その動作が開始され、デフォルト値が提供されます。 この場合、最初に辞書に変換することを検討してください。
上記の例では、テンプレート:Section.title
はsection
オブジェクトのtitle
属性に置き換えられます。
存在しない変数を使用する場合、テンプレートシステムはstring_if_invalid
オプションの値を挿入します。これは、デフォルトで(空の文字列)に設定されています。
テンプレート:Foo.bar
のようなテンプレート式の「bar」は、テンプレートコンテキストに存在する場合、変数「bar」の値を使用せずにリテラル文字列として解釈されることに注意してください。
アンダースコアで始まる変数属性は、一般にプライベートと見なされるため、アクセスできない場合があります。
フィルタ
フィルターを使用して、表示する変数を変更できます。
フィルタは次のようになります:テンプレート:Name
。 これは、テキストを小文字に変換する:tfilter: `lower` フィルターでフィルター処理された後の、テンプレート:Name
変数の値を表示します。 パイプ(|
)を使用してフィルターを適用します。
フィルタは「連鎖」させることができます。 1つのフィルターの出力が次のフィルターに適用されます。 テンプレート:Text
は、テキストコンテンツをエスケープし、改行を<p>
タグに変換するための一般的なイディオムです。
一部のフィルターは引数を取ります。 フィルタ引数は次のようになります:テンプレート:Bio
。 これにより、bio
変数の最初の30ワードが表示されます。
スペースを含むフィルター引数は引用符で囲む必要があります。 たとえば、リストをカンマとスペースで結合するには、テンプレート:List
を使用します。
Djangoには、約60の組み込みテンプレートフィルターが用意されています。 それらについては、組み込みフィルターリファレンスですべて読むことができます。 利用可能なものを味わうために、より一般的に使用されるテンプレートフィルターのいくつかを次に示します。
- :tfilter: `default`
変数がfalseまたは空の場合は、指定されたデフォルトを使用します。 それ以外の場合は、変数の値を使用します。 例えば:
{{ value|default:"nothing" }}
value
が指定されていないか空の場合、上記には「nothing
」と表示されます。- :tfilter: `length`
値の長さを返します。 これは、文字列とリストの両方で機能します。 例えば:
{{ value|length }}
value
が['a', 'b', 'c', 'd']
の場合、出力は4
になります。- :tfilter: `filesizeformat`
「人間が読める」ファイルサイズのように値をフォーマットします(つまり、
'13 KB'
、'4.1 MB'
、'102 bytes'
など)。 例えば:{{ value|filesizeformat }}
value
が123456789の場合、出力は117.7 MB
になります。
繰り返しますが、これらはほんの一例です。 完全なリストについては、組み込みフィルターリファレンスを参照してください。
独自のカスタムテンプレートフィルターを作成することもできます。 カスタムテンプレートタグとフィルターを参照してください。
も参照してください
Djangoの管理インターフェースには、特定のサイトで利用可能なすべてのテンプレートタグとフィルターの完全なリファレンスを含めることができます。 Django管理ドキュメントジェネレーターを参照してください。
テンプレートの継承
Djangoのテンプレートエンジンの最も強力な、つまり最も複雑な部分は、テンプレートの継承です。 テンプレートの継承により、サイトのすべての一般的な要素を含み、子テンプレートがオーバーライドできるブロックを定義する基本の「スケルトン」テンプレートを構築できます。
例から始めて、テンプレートの継承を見てみましょう。
<!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
と呼びます)は、2列のページに使用できるHTMLスケルトンドキュメントを定義します。 空のブロックをコンテンツで埋めるのは「子」テンプレートの仕事です。
この例では、:ttag: `block` タグは、子テンプレートが入力できる3つのブロックを定義します。 :ttag: `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 %}
ここで重要なのは:ttag: `extends` タグです。 これは、このテンプレートが別のテンプレートを「拡張」することをテンプレートエンジンに通知します。 テンプレートシステムがこのテンプレートを評価するとき、最初に親(この場合は「base.html」)を見つけます。
その時点で、テンプレートエンジンは、base.html
内の3つの:ttag: `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 %}
タグ内のコンテンツは、常にフォールバックとして使用されます。
必要な数のレベルの継承を使用できます。 継承を使用する一般的な方法の1つは、次の3レベルのアプローチです。
- サイトのメインのルックアンドフィールを保持する
base.html
テンプレートを作成します。 - サイトの「セクション」ごとに
base_SECTIONNAME.html
テンプレートを作成します。 たとえば、base_news.html
、base_sports.html
です。 これらのテンプレートはすべてbase.html
を拡張し、セクション固有のスタイル/デザインを含みます。 - ニュース記事やブログエントリなど、ページの種類ごとに個別のテンプレートを作成します。 これらのテンプレートは、適切なセクションテンプレートを拡張します。
このアプローチは、コードの再利用を最大化し、セクション全体のナビゲーションなど、共有コンテンツ領域にアイテムを追加するのに役立ちます。
継承を操作するためのヒントを次に示します。
使用する場合 :ttag: `{%extends%} ` テンプレートでは、それはそのテンプレートの最初のテンプレートタグである必要があります。 そうしないと、テンプレートの継承は機能しません。
もっと :ttag: `{%ブロック%} ` ベーステンプレートのタグの方が優れています。 子テンプレートはすべての親ブロックを定義する必要はないので、いくつかのブロックに適切なデフォルトを入力して、後で必要なものだけを定義できることを忘れないでください。 フックを少なくするよりもフックを多くする方が良いです。
多数のテンプレートでコンテンツを複製していることに気付いた場合は、そのコンテンツを親テンプレートの
{% block %}
に移動する必要があることを意味している可能性があります。親テンプレートからブロックのコンテンツを取得する必要がある場合は、
テンプレート:Block.super
変数でうまくいきます。 これは、親ブロックを完全にオーバーライドするのではなく、そのコンテンツに追加する場合に役立ちます。テンプレート:Block.super
を使用して挿入されたデータは、必要に応じて親テンプレートで既にエスケープされているため、自動的にエスケープされません(次のセクションを参照)。外部で作成された変数 :ttag: `{%ブロック%} ` テンプレートタグを使用する
as
ブロック内で構文を使用することはできません。 たとえば、このテンプレートは何もレンダリングしません。{% trans "Title" as title %} {% block content %}{{ title }}{% endblock %}
読みやすくするために、オプションで name を
{% endblock %}
タグに付けることができます。 例えば:{% block content %} ... {% endblock content %}
より大きなテンプレートでは、この手法は、どの
{% block %}
タグが閉じられているかを確認するのに役立ちます。
最後に、同じテンプレートで同じ名前の複数の:ttag: `block` タグを定義できないことに注意してください。 この制限が存在するのは、ブロックタグが「両」方向で機能するためです。 つまり、ブロックタグは、埋めるための穴を提供するだけでなく、親の穴を埋めるコンテンツも定義します。 テンプレートに同じ名前の:ttag: `block` タグが2つある場合、そのテンプレートの親は、ブロックのコンテンツのどちらを使用するかを知りません。
自動HTMLエスケープ
テンプレートからHTMLを生成する場合、結果のHTMLに影響を与える文字が変数に含まれるリスクが常にあります。 たとえば、次のテンプレートフラグメントについて考えてみます。
Hello, {{ name }}
最初は、これはユーザーの名前を表示するための無害な方法のように見えますが、ユーザーが次のように名前を入力するとどうなるかを考えてみてください。
<script>alert('hello')</script>
この名前の値を使用すると、テンプレートは次のようにレンダリングされます。
Hello, <script>alert('hello')</script>
…これは、ブラウザがJavaScriptアラートボックスをポップアップすることを意味します。
同様に、名前に'<'
記号が含まれている場合はどうなりますか?
<b>username
これにより、次のようなレンダリングされたテンプレートが作成されます。
Hello, <b>username
…これにより、Webページの残りの部分が太字になります。
明らかに、悪意のあるユーザーがこの種の穴を使用して潜在的に悪いことをする可能性があるため、ユーザーが送信したデータを盲目的に信頼してWebページに直接挿入するべきではありません。 このタイプのセキュリティエクスプロイトは、クロスサイトスクリプティング(XSS)攻撃と呼ばれます。
この問題を回避するには、次の2つのオプションがあります。
- 1つは、信頼できない各変数を:tfilter: `escape` フィルター(以下に記載)を介して実行することです。このフィルターは、潜在的に有害なHTML文字を無害な文字に変換します。 これは最初の数年間はDjangoのデフォルトのソリューションでしたが、問題は、開発者/テンプレートの作成者である you に責任を負わせて、すべてを確実にエスケープすることです。 データをエスケープするのを忘れがちです。
- 2つ目は、Djangoの自動HTMLエスケープを利用できることです。 このセクションの残りの部分では、自動エスケープがどのように機能するかについて説明します。
Djangoのデフォルトでは、すべてのテンプレートがすべての変数タグの出力を自動的にエスケープします。 具体的には、次の5文字はエスケープされます。
<
は<
に変換されます>
は>
に変換されます'
(一重引用符)は'
に変換されます"
(二重引用符)は"
に変換されます&
は&
に変換されます
繰り返しになりますが、この動作はデフォルトでオンになっていることを強調します。 Djangoのテンプレートシステムを使用している場合は、保護されています。
オフにする方法
サイトごと、テンプレートごと、または変数ごとのレベルでデータを自動エスケープしたくない場合は、いくつかの方法でデータをオフにすることができます。
なぜそれをオフにしたいのですか? テンプレート変数には、生のHTMLとしてレンダリングする意図するデータが含まれている場合があります。その場合、その内容をエスケープしたくありません。 たとえば、データベースにHTMLのブロブを格納し、それをテンプレートに直接埋め込みたい場合があります。 または、Djangoのテンプレートシステムを使用して、たとえば電子メールメッセージのようにではなく HTMLのテキストを生成している可能性があります。
個々の変数の場合
個々の変数の自動エスケープを無効にするには、:tfilter: `safe` フィルターを使用します。
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
safe は、 safeの省略形であると考えてくださいまたはは、HTML として安全に解釈できます。 この例では、data
に'<b>'
が含まれている場合、出力は次のようになります。
This will be escaped: <b>
This will not be escaped: <b>
テンプレートブロックの場合
テンプレートの自動エスケープを制御するには、次のように、テンプレート(またはテンプレートの特定のセクション)を:ttag: `autoescape` タグでラップします。
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
:ttag: `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 %}
自動エスケープタグは、すべてのブロックタグと同様に、現在のテンプレートを拡張するテンプレートと、:ttag: `include` タグを介して含まれるテンプレートにその効果を渡します。 例えば:
{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
{% 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側の開発者(ビューやカスタムフィルターを作成する人)は、データをエスケープしてはならない場合を考え、データに適切にマークを付ける必要があるため、テンプレートで機能するだけです。
自動エスケープが有効になっているかどうかわからない状況で使用される可能性のあるテンプレートを作成している場合は、エスケープが必要な変数に:tfilter: `escape` フィルターを追加します。 自動エスケープがオンの場合、:tfilter: `escape` フィルター二重エスケープデータ– :tfilter:` escape` フィルターの危険はありません。自動エスケープされた変数には影響しません。
文字列リテラルと自動エスケープ
前に述べたように、フィルター引数は文字列にすることができます。
{{ data|default:"This is a string literal." }}
すべての文字列リテラルは挿入され、は自動的にテンプレートにエスケープされません。これらは、すべて:tfilter: `safe` フィルターを通過したかのように機能します。 この背後にある理由は、テンプレートの作成者が文字列リテラルに入力する内容を制御しているため、テンプレートの作成時にテキストが正しくエスケープされていることを確認できるためです。
これはあなたが書くことを意味します
{{ data|default:"3 < 2" }}
…それよりも:
{{ data|default:"3 < 2" }} {# Bad! Don't do this. #}
これは、変数自体からのデータに何が起こるかには影響しません。 変数の内容は、テンプレートの作成者の制御が及ばないため、必要に応じて自動的にエスケープされます。
メソッド呼び出しへのアクセス
オブジェクトにアタッチされたほとんどのメソッド呼び出しは、テンプレート内からも利用できます。 これは、テンプレートが、ビューから渡されたクラス属性(フィールド名など)や変数以外にもアクセスできることを意味します。 たとえば、Django ORMは、外部キーに関連するオブジェクトのコレクションを検索するための“ entry_set” 構文を提供します。 したがって、「タスク」と呼ばれるモデルと外部キーの関係を持つ「コメント」と呼ばれるモデルが与えられると、次のように特定のタスクに添付されたすべてのコメントをループできます。
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
同様に、 QuerySets は、それらに含まれるオブジェクトの数をカウントするためのcount()
メソッドを提供します。 したがって、次のコマンドを使用して、現在のタスクに関連するすべてのコメントの数を取得できます。
{{ task.comment_set.all.count }}
そしてもちろん、独自のモデルで明示的に定義したメソッドに簡単にアクセスできます。
class Task(models.Model):
def foo(self):
return "bar"
{{ task.foo }}
Djangoは、テンプレート言語で使用できるロジック処理の量を意図的に制限しているため、テンプレート内からアクセスされるメソッド呼び出しに引数を渡すことはできません。 データはビューで計算してから、表示用のテンプレートに渡す必要があります。
カスタムタグおよびフィルターライブラリ
特定のアプリケーションは、カスタムタグおよびフィルターライブラリを提供します。 テンプレートでそれらにアクセスするには、アプリケーションが:setting: `INSTALLED_APPS` にあることを確認し(この例では'django.contrib.humanize'
を追加します)、:ttag:を使用します。テンプレートの `load` タグ:
{% load humanize %}
{{ 45000|intcomma }}
上記では、:ttag: `load` タグがhumanize
タグライブラリをロードし、intcomma
フィルターを使用できるようにします。 django.contrib.admindocs を有効にしている場合は、管理者のドキュメント領域を参照して、インストール内のカスタムライブラリのリストを見つけることができます。
:ttag: `load` タグは、スペースで区切って複数のライブラリ名を使用できます。 例:
{% load humanize i18n %}
独自のカスタムテンプレートライブラリの作成については、カスタムテンプレートタグとフィルターを参照してください。
カスタムライブラリとテンプレートの継承
カスタムタグまたはフィルターライブラリをロードすると、タグ/フィルターは現在のテンプレートでのみ使用可能になり、テンプレート継承パスに沿った親または子テンプレートは使用できなくなります。
たとえば、テンプレートfoo.html
に{% load humanize %}
がある場合、子テンプレート(たとえば、{% extends "foo.html" %}
があるテンプレート)はではなくヒューマナイズテンプレートにアクセスできます。タグとフィルター。 子テンプレートは、それ自体の{% load humanize %}
を担当します。
これは、保守性と健全性のための機能です。
コメント
テンプレート内の行の一部をコメントアウトするには、コメント構文
{# #}
を使用します。たとえば、このテンプレートは
'hello'
としてレンダリングされます。コメントには、無効かどうかに関係なく、任意のテンプレートコードを含めることができます。 例えば:
この構文は、単一行コメントにのみ使用できます(
{#
と#}
の区切り文字の間に改行は使用できません)。 テンプレートの複数行部分をコメントアウトする必要がある場合は、:ttag: `comment` タグを参照してください。