概要
個別ページで共通する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には何も書いていない
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% block title %}Placeholeder{% endblock %}</title> </head> <body> <header> {% block header %} <p>Default Header Menu</p> <ul> <li>Login</li> <li>Signup</li> </ul> {% endblock %} </header> {% block content %} {% endblock %} </body> </html> |
子テンプレート
子テンプレート(index.html)ではextendsタグによって親テンプレートを継承する。この場合のタグの引数は、view関数で呼ぶrender文の第2引数と同じで、テンプレートディレクトリーからの相対パス。
|
1 |
{% extends 'inheritance/layout.html' %} |
子テンプレートはこの1行だけなので、単にこのテンプレートの内容が親テンプレートの内容で埋められる。
ただし親テンプレートのblockタグに対する子テンプレートのblockがないので、以下のように解釈される。
- 親の
blockに内容が定義されていれば、その内容が展開される - 親の
blockがタグだけで内容がなければ、何も引き継がれない。
子テンプレートが引き継いだ親テンプレートの内容が解釈された結果、最終的なindex.htmlは以下のようになる。
title要素には'Placeholder'の文字が充てられるheader要素には、親で記述されたp要素とリストが埋められる- 親の
{% block content %}のブロックには内容がないので、子要素にも何も引き継がれない
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Placeholeder</title> </head> <body> <header> <p>Default Header Menu</p> <ul> <li>Login</li> <li>Signup</li> </ul> </header> </body> </html> |
この結果、ブラウザーの表示は以下のようになる(ブラウザーのタブには'Placeholder'の文字が表示される。
Default Header Menu
- Login
- Signup
標準形
次に、子テンプレートindex.htmlで親テンプレートの各blockの具体の内容を実装する。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{% extends 'inheritance/layout.html' %} {% block title %}タイトル{% endblock %} {% block header %} <p>メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> {% endblock %} {% block content %} <p>サイト内容</p> <ul> <li>サイト紹介</li> <li>内容一覧</li> </ul> {% endblock %} |
この結果、親テンプレートのblock部分に子テンプレートの対応するblockの内容が埋め込まれる。親テンプレートのblockに書かれた内容がある場合は、子テンプレートの内容で置き換えられる。
HTMLソースを確認すると、子テンプレートの内容が反映されていることがわかる。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>タイトル</title> </head> <body> <header> <p>メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> </header> <p>サイト内容</p> <ul> <li>サイト紹介</li> <li>内容一覧</li> </ul> </body> </html> |
ブラウザーでの表示結果は以下のとおり(ブラウザーのタブには’タイトル'と表示される)。
- ログイン
- サインアップ
サイト内容
- サイト紹介
- 内容一覧
継承のチェーン
これまでは親~子の継承だったが、さらに親~子~孫の継承を見てみる。
たとえばページの種類が、認証済みユーザー用と非認証のゲスト用の2通りに区分され、それぞれでメニューの内容を変えたいとする。このような場合は、以下のようにテンプレートを分けて継承させる。
- 親:全てのページに共通するテンプレート
- 子:認証用/非認証用それぞれに応じたテンプレート
- 孫:個々のページ~ページ内容によって認証用/非認証用あり
認証済みユーザー用のページ
まず、認証済みユーザー用のメニューを共通化し、これをユーザーホーム(index)で継承する例。先の例ではindex.htmlがlayout.htmlを継承したが、ここでは以下のように構成を変更する。
- 前:
layout.html←index.html - 後:
layout.html←authenticated.html←index.html
サイト共通の基本的な枠組みはlayout.htmlで記述し、ユーザー認証中のHTML部分はauthenticated.htmlで、認証中の具体のページ内容をindex.htmlで実装している。
親テンプレートlayout.htmlはそのままとし、新たにauthenticated.htmlを以下の内容で準備する。ここでは{% block header %}のみを実装し、認証済みユーザー用のヘッダーメニューを表示させる。
|
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/layout.html' %} {% block header %} <p>ユーザー用メニュー</p> <ul> <li>ユーザーホーム</li> <li>ログアウト</li> </ul> {% endblock %} |
index.htmlはauthenticated.htmlを継承し、孫テンプレートとなる。未実装の{% block title %}と{% block content %}を実装して「ログイン済みユーザー用の内容」とだけ表示させている。
|
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/authenticated.html' %} {% block title %}ようこそ{% endblock %} {% block content %} <h1>ユーザーホームページ</h1> <p>ログイン済みユーザー用の内容</p> {% endblock %} |
index.htmlを呼び出すと、index.htmlでcontentとtitleが、authenticated.htmlでheaderが実装され、以下のように表示される。
ユーザー用メニュー
- ユーザーホーム
- ログアウト
ユーザーホームページ
ログイン済みユーザー用の内容
非認証時のページ
次に非認証時のページとして、以下のような構成を追加する。
layout.html←unauthenticated.html←login.html
全体共通の基本的な枠組みlayout.htmlは同じで、非認証時のHTML部分をuauthenticated.htmlにまとめ、非認証時の具体ページの例としてlogin.htmlを追加。
unauthenticated.htmlはauthenticated.htmlと同じように{% block header %}のみを実装し、ゲスト用のメニューを記述。
|
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/layout.html' %} {% block header %} <p>ゲスト用メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> {% endblock %} |
ログインページでは非認証時のunauthenticated.htmlを継承し、具体のタイトルと内容を記述。
|
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/unauthenticated.html' %} {% block title %}ログイン{% endblock %} {% block content %} <h1>ログインページ</h1> <p>ユーザーログイン用の内容</p> {% endblock %} |
表示結果は以下のようになる。
ゲスト用メニュー
- ログイン
- サインアップ
ログインページ
ユーザーログイン用の内容
重複記述の禁止
blockタグの識別子が同じ名前で複数存在することはできない。たとえばtitle要素とページのh1要素が同じ内容なので以下のように2度使いしようとするとエラーになる。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% block title %}Placeholeder{% endblock %}</title> </head> <body> {% comment %} <h1>{% block title %}Title Here{% endblock %}</h1> 同じブロック名のブロックは重複して書けない TemplateSyntaxError at /inheritance 'block' tag with name 'title' appears more than once {% endcomment %} </body> </html> |