Django – DTL – include

テンプレートの継承では、個別ページが共有するテンプレートを継承元の親テンプレートとした。一方、HTMLの一部を部品化して任意のページに埋め込むにはincludeタグを使う。

たとえば検索フォームを作成して、これを複数のページで使い回したいとする。例として以下のようなダミーのHTMLを準備する。

テンプレートの継承で使った以下のテンプレートを考える。includeタグで挿入するHTMLのパスを指定している。パスの指定の仕方は他のタグと同じ相対パス。

このページを呼び出すと、以下のようにincludeタグの部分に検索フォームのHTMLが埋め込まれる。

表示は以下の通りで、検索フォームの部分が表示されている。

ユーザー用メニュー

  • ユーザーホーム
  • ログアウト

ユーザーホームページ

ログイン済みユーザー用の内容

検索用のフォーム

 

Django – DTL – 継承

概要

個別ページで共通するHTMLの枠組みがある場合、それらを親テンプレートとして一つにまとめて、個別ページは親テンプレートを継承するようにできる。たとえばHTML全体の枠組みやサイトに共通するデザインなどをまとめるケース。テンプレートの継承の要点は以下のとおり。

  • 継承元の親テンプレートを準備
    • blockタグ→子テンプレートで具体内容を埋め込むブロック
  • 継承先の子テンプレートを準備
    • extends→継承元の親テンプレートを指定
    • blockタグ→親テンプレートのblockタグの部分に埋め込む内容
  • blockタグには識別名を書き、親・子でブロックを対応させる

子テンプレートを呼び出したとき、親テンプレートのblockタグの部分に子テンプレートのblockタグの内容が埋め込まれたHTMLとなる。

なお継承ではないが、includeタグによって別に準備したHTMLの一部分を取り込むことができる。

基本形

まず、子テンプレートで単に親テンプレートをextendsで読み込むだけの例を確認する。

継承元の親テンプレート

子テンプレートが継承する元の親テンプレート(layout.html)を以下のように準備する。要点は以下のとおり。

  • HTMLの完全な枠組みを持たせている
  • title要素、header要素、body要素にblockタグのブロックを書いている
    • title要素のblockには'Placeholder'という文字列を書いている
    • header要素のblockにはp要素とリストを書いている
    • body要素のblockには何も書いていない

子テンプレート

子テンプレート(index.html)ではextendsタグによって親テンプレートを継承する。この場合のタグの引数は、view関数で呼ぶrender文の第2引数と同じで、テンプレートディレクトリーからの相対パス。

子テンプレートはこの1行だけなので、単にこのテンプレートの内容が親テンプレートの内容で埋められる。

ただし親テンプレートのblockタグに対する子テンプレートのblockがないので、以下のように解釈される。

  • 親のblockに内容が定義されていれば、その内容が展開される
  • 親のblockがタグだけで内容がなければ、何も引き継がれない。

子テンプレートが引き継いだ親テンプレートの内容が解釈された結果、最終的なindex.htmlは以下のようになる。

  • title要素には'Placeholder'の文字が充てられる
  • header要素には、親で記述されたp要素とリストが埋められる
  • 親の{% block content %}のブロックには内容がないので、子要素にも何も引き継がれない

この結果、ブラウザーの表示は以下のようになる(ブラウザーのタブには'Placeholder'の文字が表示される。

Default Header Menu

  • Login
  • Signup

標準形

次に、子テンプレートindex.htmlで親テンプレートの各blockの具体の内容を実装する。

この結果、親テンプレートのblock部分に子テンプレートの対応するblockの内容が埋め込まれる。親テンプレートのblockに書かれた内容がある場合は、子テンプレートの内容で置き換えられる。

HTMLソースを確認すると、子テンプレートの内容が反映されていることがわかる。

ブラウザーでの表示結果は以下のとおり(ブラウザーのタブには’タイトル'と表示される)。

メニュー

  • ログイン
  • サインアップ

サイト内容

  • サイト紹介
  • 内容一覧

 

継承のチェーン

これまでは親~子の継承だったが、さらに親~子~孫の継承を見てみる。

たとえばページの種類が、認証済みユーザー用と非認証のゲスト用の2通りに区分され、それぞれでメニューの内容を変えたいとする。このような場合は、以下のようにテンプレートを分けて継承させる。

  • 親:全てのページに共通するテンプレート
  • 子:認証用/非認証用それぞれに応じたテンプレート
  • 孫:個々のページ~ページ内容によって認証用/非認証用あり

認証済みユーザー用のページ

まず、認証済みユーザー用のメニューを共通化し、これをユーザーホーム(index)で継承する例。先の例ではindex.htmllayout.htmlを継承したが、ここでは以下のように構成を変更する。

  • 前:layout.htmlindex.html
  • 後:layout.htmlauthenticated.htmlindex.html

サイト共通の基本的な枠組みはlayout.htmlで記述し、ユーザー認証中のHTML部分はauthenticated.htmlで、認証中の具体のページ内容をindex.htmlで実装している。

親テンプレートlayout.htmlはそのままとし、新たにauthenticated.htmlを以下の内容で準備する。ここでは{% block header %}のみを実装し、認証済みユーザー用のヘッダーメニューを表示させる。

index.htmlauthenticated.htmlを継承し、孫テンプレートとなる。未実装の{% block title %}{% block content %}を実装して「ログイン済みユーザー用の内容」とだけ表示させている。

index.htmlを呼び出すと、index.htmlcontenttitleが、authenticated.htmlheaderが実装され、以下のように表示される。

ユーザー用メニュー

  • ユーザーホーム
  • ログアウト

ユーザーホームページ

ログイン済みユーザー用の内容

 

非認証時のページ

次に非認証時のページとして、以下のような構成を追加する。

  • layout.htmlunauthenticated.htmllogin.html

全体共通の基本的な枠組みlayout.htmlは同じで、非認証時のHTML部分をuauthenticated.htmlにまとめ、非認証時の具体ページの例としてlogin.htmlを追加。

unauthenticated.htmlauthenticated.htmlと同じように{% block header %}のみを実装し、ゲスト用のメニューを記述。

ログインページでは非認証時のunauthenticated.htmlを継承し、具体のタイトルと内容を記述。

表示結果は以下のようになる。

ゲスト用メニュー

  • ログイン
  • サインアップ

ログインページ

ユーザーログイン用の内容

重複記述の禁止

blockタグの識別子が同じ名前で複数存在することはできない。たとえばtitle要素とページのh1要素が同じ内容なので以下のように2度使いしようとするとエラーになる。

 

Django – DTL – if

基本形

文法

基本はPythonのif ... elif ... elseと同じで、終了タグ{% endif %}で終える。

テンプレートの実例は以下の通り。

Pythonとの違い

以下はPythonのif文との違いで、外すとTemplateSyntaxErrorになる。

  • 条件部に()は使えない
    • {% if (he == gentle or she == tender) and they == love_each_other %} はエラー
    • こういう場合はifのネストにする
  • 条件部に算術演算子は使えないがフィルターは使える
    • {% if d % 2 == 2 %}はエラー
    • {% if d|divisable:2 %}はok

その他

ifequal/ifnotequal

if a == b/if a != bと同じ。

ifchanged

割愛。

 

 

 

Django – DTL – フィルターとスペースとエラー

概要

DjangoのDTLにおけるフィルターの一般形式は以下のようになっている。

変数|フィルター:引数|フィルター:引数|・・・

フィルターは変数展開{{ .... }}でもタグ{% .... %}でも使えるが、それぞれで'|'':'の前後にスペースを入れた時の挙動が異なるので整理する。

結論すると、DTLでフィルターを使う場合は'|'':'の前後にはスペースを入れないと決めておくのが紛れがなくてよい。

変数展開の場合

'|'の前後のスペースは許されるが、':'の前後にスペースを入れるとTemplateSyntaxErrorになる。

{{ var|add:2 }} Ok
{{ var |add:2 }} Ok
{{ var| add:2 }} Ok
{{ var|add :2 }} Error
{{ var|add: 2 }} Error

タグの場合

'|'の前後も':'の前後もスペースを入れるとTemplateSyntaxErrorになる。

{% if var|divisibleby:2 %} Ok
{% if var |divisibleby:2 %} Error
{% if var| divisibleby:2 %} Error
{% if var|divisibleby :2 %} Error
{% if var|divisibleby: 2 %} Error

 

Django – DTL – テンプレート言語

概要

Djangoのテンプレート言語(DTL: Django Template Language)について整理する。

テンプレートの基礎についてはこちら

コメント

一行コメント{#...#}と複数行コメント{% comment %}...{% endcomment %}がある。

DTLのコメントの詳細についてはこちら

変数展開

変数の内容を展開するには{{ 変数 }}と記述する。

変数の内容は、数値・文字列のほか、コレクションの要素や一般のオブジェクトの属性やメソッドの結果も展開可能。

変数展開の詳細についてはこちら

タグ

DTLのタグは{% タグ %}と記述する。

主なタグには以下のようなものがある。

特殊機能

制御

URL指定

 

Django – DTL – for

基本形

文法

forタグで始め、endforタグで終える。forタグの書き方はPythonのfor文と同じ。

コレクションに対する操作の殆どはPythonと同じ。

リスト

リストの内容を順次取り出す例。

メダル

  1. Gold medal
  2. Silver medal
  3. Bronze medal

 

アンパック

サイコロ: (1, 3) (3, 3) (4, 2)

辞書

辞書のキーと値を取り出す例。Pythonと同じで.itemsを付ける。

首都

  • Country: Japan – Capital: Tokyo
  • Country: France – Capital: Paris
  • Country: US – Capital: WDC

注意点

for ... inの後には関数やリテラルは書けず、rangezipenumerateはエラーになる。ただしリテラルは書けないが、view関数内で変数に代入しておけば実行可能。以下はrangeの例。

zipenumerateについても、view関数側で変数に代入しておけば使える。なおenumerateについては、forloop変数にカウンターが用意されている。

empty~変数が空の場合

{% empty %}タグは{% for %}~{% endfor %}の間に入れる。forで使う変数が空の場合にループを実行せず、empty以下が実行されてループを終わる。

変数の内容が以下の場合にemptyブロックが実行される。

  • 未定義
  • None
  • []
  • ()
  • ''

reversed~逆順

forタグにreversedを付すと、cの内容が逆順でeに取り出される。

forloop変数

一覧

forloop変数によって、ループ中でカウンター値やループの先頭・末尾データなどを得ることができる。

forloop.counter ループカウンター値(初期値:1)
forloop.counter0 ループカウンター値(初期値:0)
forloop.revcounter 逆順カウンター値(終了値:1)
forloop.revcounter0 逆順カウンター値(終了置:0)
forloop.first 最初のループならTrue
forloop.last 最後のループならTrue
forloop.parentloop 多重ループの外側の値

カウンター

以下はrevcounter0の例。

countdown

  • 3-three
  • 2-two
  • 1-one
  • 0-zero!

first/last~最初/最後のループ判定

forloop.firstforloop.lastは最初や末尾のループだけ別の処理をさせたいときに便利。

first and last

<1, 2, 3, 4>

この例では、最初の要素の前に'<'を出力し、最後の要素の後ろにカンマを付けずに'>'を出力する。

parentloop~親ループ

foroop.parentloopは言わば「親ループの~」という意味で、この後にcounterfirstなどを付けて親ループの値を参照する。たとえば、

  • forloop.parentloop.counter:親ループの現在のカウンター値
  • forloop.parentloop.first:親ループが現在最初のループ中ならTrue

たとえば以下の例では、二重ループを回しながら、一番最初の行の先頭要素の前と一番最後の行の末尾要素の後に別の文字を出力させている。

parentloop

< 1-1 1-2 1-3
2-1 2-2 2-3
3-1 3-2 3-3 >

 

 

Django – DTL – コメント

コメント

一行コメントは{#...#}。コメント内で改行はできない。

複数行コメントは{% comment %} ... {% endcomment %}

コメント自体の注釈を書くこともできる。

 

Django – DTL – 変数展開

概要

Djangoのテンプレート内での変数展開は{{ 変数 }}と記述。

  • 単純変数はそのまま内容が展開される
  • コレクションはリスト、タプル、辞書に応じた表現になる
  • コレクションの要素は[]ではなくドット(.)で添字やキーを指定
  • オブジェクトの属性やメソッドもドット(.)で指定

単純変数

単純変数の展開の例を以下に示す。まずview関数。

テンプレートの変数展開部分。

ブラウザーの表示。

単純変数

  • 実数値:3.1415
  • 文字列:ABCDいろはに

 

コレクション

コレクションを参照する変数を展開すると、変数をprint文でコンソール出力した時の形式で展開される。

テンプレートの変数展開部分。

ブラウザーの表示。

コレクション

  • リスト:[‘one’, 2, [3, ‘three’]]
  • タプル:(‘one’, 2, (3, ‘three’))
  • 辞 書:{‘one’: 1, ‘two’: 2, ‘three’: 3}

 

コレクションの要素を展開する場合、Pythonの[]と違ってドット(.)に続けて添字の数値やキーの文字列をそのまま書く。

多次元の場合には、次数に対応して連ねていく。

コレクション全体と同じ例で要素を表示したいときのテンプレートの例。

ブラウザーの表示。

コレクションの要素

  • リストの要素:one, 2, 3, three
  • タプルの要素:one, 2, 3, three
  • 辞書の要素 :1, 2, 3

オブジェクト

クラスのインスタンスを参照する変数も展開できる。以下はviews.pyで単純なクラスを定義して、そのインスタンスをテンプレートに渡している。

 

テンプレートでのオブジェクトの展開は、以下の3種類となる。

  • オブジェクト全体を展開すると、オブジェクトが文字列にキャストされた表現となる
  • オブジェクトの属性は、変数と属性名をドット(.)で繋いで得られる
  • オブジェクトとメソッド名をドット(.)で繋ぐと、メソッドの実行結果が得られる

ブラウザーでの表示。

オブジェクト

  • オブジェクト全体:<dtldemo.views.BinaryOperation object at 0x7fdf2835ec50>
  • オブジェクトの属性:2, 3
  • オブジェクトのメソッド:5

フィルター

基本形

フィルターはテンプレート中の変数を加工するもので、Djangoによる組み込みフィルターが多数用意されている。フィルターの基本形は以下のとおり。

{{ 変数|フィルター }}

わかりやすい例としては、変数の文字列の文字数を返すlengthがある。以下のテンプレートでは、var_stringに格納されている文字列とその文字数を表示している。

変数の内容が'abcd'の場合、表示は以下のようになる。

引数

フィルターには引数を持つものもある。引数はリテラルとする必要がある。

{{ 変数|フィルター:引数 }}

以下の例はaddフィルターで、元の変数の内容に引数の内容を加える。

var_two=2var_string='abcde'の場合、表示は以下のようになる。

チェーン

フィルターのチェーンによって、複数のフィルターを作用させることができる。

{{ 変数|フィルター[:引数]|フィルター[:引数] ... }}

以下の例では変数の文字列に文字列定数を連結した上で文字数を得ている。

var_string='abcde'の場合、結果は’8'となる。

スペースとエラー

'|'':'の前後にスペースを入れても問題ないが、タグ({% ... %})でフィルターを使う場合にはエラーとなる場合がある

紛らわしいので、「フィルターを使うときは間のスペースなし」と決めておく方がよい。

 

Django – テンプレートの基礎

概要

HTMLや埋め込まれたデータを含むテンプレートを使ったアプリケーションの基本。流れは以下の通り。

  • テンプレートを表示させるアプリケーションの作成と登録
  • テンプレートを保存するディレクトリーとテンプレートファイルの作成
  • テンプレートを呼び出すビュー関数の作成
  • ルーティング設定

ここでは以下のことを整理している。

  • テンプレートの配置場所
  • テンプレートの参照方法
  • テンプレート言語(DTL)の概要

関連記事

プロジェクトの準備

プロジェクトディレクトリーをtemplatebasicsとし、settings.pyなどを含むサブディレクトリーをconfigとする。

初期設定とサーバー起動で動作確認しておく。

アプリケーションの作成・登録

テンプレートを表示させるアプリケーションtemplatedemoを作成する。

そしてアプリケーションをsettings.pyINSTALLED_APPSに追加登録する。

テンプレート配置

初期設定

テンプレートの配置場所settings.pyTEMPLATESで設定する。

DIRSは任意の場所・ディレクトリー名のパスを設定する。またAPP_DIRTrueに設定すると、各アプリケーションディレクトリー下のtemplatesディレクトリーが探索される。

複数のテンプレートディレクトリーは一括して同列に扱われ、各ディレクトリーのサブディレクトリーも再帰的に探索される。

デフォルトの設定では、各アプリケーションディレクトリー下のtemplatesディレクトリーのみが対象となっている。

配置場所

ここでは、以下の場所にテンプレートディレクトリーを置いて設定していく。

  • 2か所にテンプレートディレクトリーを配置し、いずれのファイル名ともindex.htmlとしている
  • プロジェクト直下のディレクトリー名は任意だが、簡明のためtemplatesとする
  • 2つのテンプレートディレクトリーは併せて参照されるので、アプリケーション下の方は区別のためにサブディレクトリーを設けている

アプリケーション下

テンプレートディレクトリー作成

作成したアプリケーションディレクトリーtemplatedemoの下にtemplatesディレクトリーを作成し、その下に更にサブディレクトリーtemplatedemoを作成する。

テンプレートファイル作成

作成したサブディレクトリー下にindex.htmlファイルを作成して以下を記述。

プロジェクト直下

settings.py編集

TEMPLATESDIRSにプロジェクト直下のtemplatesディレクトリーを探索するよう追加。

os.pathを使う方法もありその場合はimport osが必要になるが、上記の記述でも同じ効果。

テンプレートディレクトリー作成

プロジェクトディレクトリー直下にtemplatesディレクトリーを作成する。サブディレクトリーは作成しない。

テンプレートファイル作成

作成したtemplatesディレクトリー下にindex.htmlファイルを作成して以下を記述。

view関数の作成とルーティング

view関数の作成

アプリケーションディレクトリーtemplatedemoviews.pyを編集して、view関数を定義する。

view関数でレンダリングするテンプレートを指定するため、プロジェクト直下とアプリケーションディレクトリー下2か所のテンプレートに対応する関数を記述している。要点は以下のとおり。

  • def 関数名(request):で定義
  • テンプレートを呼ぶ戻り値はrender関数
    • 第1引数は受け取ったrequest
    • 第2引数はテンプレートファイルのパス
  • テンプレートファイルのパスについて
    • TEMPLATESで設定したすべてのテンプレートディレクトリー下が一括して扱われる
    • テンプレートディレクトリー以下の相対パスとする

複数テンプレートディレクトリーが一括して扱われ、テンプレートの指定がテンプレートディレクトリーからの相対パスとなるため、サブディレクトリーを設定した意義が確認できる。

view関数のルーティング登録

これらのview関数をURLから呼び出すためurls.pyに記述を追加する。

確認

ここで開発用Webサーバーを起動して、ブラウザーで以下の入力に対応したページが表示されるのが確認できる。

  • localhost:8000/project
  • localhost:8000/application

DTL~テンプレート言語

概要

テンプレートにはHTMLのほかに、view関数から受け取った変数を埋め込んだり、繰り返しや条件分岐による出力の変更といった制御をさせることができる。そのための言語をDTL (Django Template Language)と言う。

  • {{ 変数 }}:変数の内容を展開してHTMLに埋め込む
  • {% 文 %}forendforによるループやifelifendifによる条件分岐で出力を制御する

変数の展開

たとえばtemplatedemoアプリケーションのviews.pyで、index_application関数を以下のように変更する。

ここでは、変数textを定義して、これを'text'をキーとする辞書の値としてrender関数の第3引数に渡している。

次に、templatedemoアプリケーション下のindex.htmlテンプレートを以下のように変更する。

表示結果は、ブラウザーの上部またはタブと見出しに同じ「アプリケーション下」が表示される。

このように、ビュー側で処理した結果を変数としてテンプレートに渡し、HTMLとして表示させることができる。

制御構造

先のindex_application関数を以下のように変更する。

ここでは第3引数をcontextとし、この変数に2つの要素を持つ辞書を入れている。1つ目の要素はタイトル、2つ目の要素は物語のシーンを収めた配列としている。

これを受け取るindex.htmlを以下のように変更する。

scenesで渡された配列の要素を、DTLのforendforでループしながら取り出し、li要素として並べている。この結果、出力されたHTMLでは5つのシーンがリストとして表示される。

同じテンプレートを使って、タイトルとシーンを桃太郎やかぐや姫などと変化させることができる。

 

Django – テンプレートの場所

概要

  • Djangoのテンプレートファイルは、テンプレートディレクトリーやそのサブディレクトリーに置かれることを想定している
  • 複数場所に配置されたテンプレートディレクトリー以下が一括して探索される
  • 探索場所と順序はsettings.pyファイルのTEMPLATESの設定に従う
    • DIRSでは任意の場所・名前でテンプレートディレクトリーを追加できる
    • APP_DIRSTrueに設定されている場合、各アプリケーションディレクトリー下のtemplatesディレクトリーが探索される
  • 探索されたテンプレートを参照する場合、テンプレートディレクトリーからの相対パスで指定する
  • テンプレートディレクトリーを分散配置しても一括して検索されるため、各テンプレートディレクトリーに適切なサブディレクトリーを置いて、その下にテンプレートファイルを置くのが一般的

確認用プロジェクト

確認のためtemplatetestプロジェクトを準備し、app1app2の2つのアプリケーションを作成する。templatesディレクトリーとそのサブディレクトリーを以下のように作成し、4つの場所に即した内容のindex.htmlファイルを置く。

各場所のindex.htmlは、title要素/bodyp要素で以下を表示させるようにした。

  • App1/Under the app1 directory
  • App2/Under the app2 directory
  • Subirectory/Under the subdirectory
  • Base Directory/Under the base directory

そしてapp1のビューファイルで指定するテンプレート名のディレクトリー名を変更しながら、各場所のindex.htmlが読み込まれたかどうか確認する。

プロジェクトの完成後、template変数に設定した文字列を以下のように変更して、それぞれに対応したページが表示されるかどうかを確認する。

  • 'base/index.html'
  • 'subdirectory/index.html'
  • 'app1/index.html'
  • 'app2/index.html'

TEMPLATESの初期設定

初期状態でのTEMPLATESは以下の通り。

このうちテンプレートの探索場所は'DIRS''APP_DIRS'で設定される。

なおディレクトリーの探索場所が設定されていれば、そのサブディレクトリー下も再帰的に探索される。

DIRSでの設定

プロジェクトディレクトリー直下

'DIRS'にはプロジェクト内任意の場所の、任意の名前のディレクトリーを設定できる。たとえばプロジェクトディレクトリー直下に置いたtemplatesを探索させるなら以下のように記述する。

または

後者の場合、settings.pyではimport osがないので追加が必要。

この設定により、'templatetest/templates/base/index.html'のレンダリングが可能になる。

サブディレクトリー下

プロジェクトの設定ファイル群があるサブディレクトリー下(この場合configディレクトリー)にあるディレクトリーを探索させるときは以下のように相対パスで設定する。

または

この場合もsettings.pyimport osが必要。

この設定により、templatetest/config/subdirectory/templates/index.htmlのレンダリングが可能になる。

APP_DIRSの設定

また、'APP_DIRS'Trueに設定されている場合は、各アプリケーションディレクトリー下にあるtemplatesディレクトリーが探索される。

デフォルトでTrueがセットされているので、アプリケーションディレクトリー下にtempratesディレクトリーがあれば全て探索される。

この例の場合、app1/templatesapp2/templatesは、デフォルトの設定)のままでそれぞれのサブディレクトリーも含めて探索される。

この結果templatetest/app1/templates/app1/index.htmltemplatetest/app2/templates/app2/index.htmlのレンダリングが可能になる。

全てを探索させるTEMPLATESの設定

これまでの4つの場所のすべてを探索対象とするには、settings.pyTEMPLATESを以下のように設定する。

この結果、以下の4つが探索可能になる。

  • templatetest/templates/base/index.html
  • templatetest/config/templates/subdirectory/index.html
  • templatetest/app1/templates/app1/index.html
  • templatetest/app2/templates/app2/index.html

templatesのサブディレクトリー

特にアプリケーションディレクトリー下にテンプレートを置く場合に、(index.htmlのように)同じ名前のファイルが重複しないよう、サブディレクトリーを設けるのが一般的。

通常、サブディレクトリー名はアプリケーションディレクトリー名と同じとする。冗長だが重複が防止され、可読性も保たれる。