概要
個別ページで共通する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> |