テンプレートエンジンの概要と種類
WebフレームワークのMVCモデルのViewに相当する機能を担うのがテンプレートエンジンです。Javaのフレームワークでは、JSPに相当します。コントローラが生成したデータを、テンプレートエンジンが解釈して、ユーザにHTMLやXMLとして配信します。
Pythonのテンプレートエンジンは大きく2種類に分類できます。
一つは、XMLベースのテンプレート言語(アトリビュート言語とも呼びます)です。XMLテンプレートエンジンとしては、Zope Page TemplateやKidやGenshiが有名です。特に、GenshiはTracの最新版やWebフレームワークのTurboGearsの次期バージョンのデフォルトテンプレートエンジンとして採用されるなど、注目度が高い言語です。XMLテンプレートエンジンは、DreamWeaverなどのグラフィカルなHTMLエディタとの親和性が売りの一つになっています。作成するテンプレートは正しいXMLである必要があります。条件による分岐やループ処理、HTMLタグの値の置換などをXMLのアトリビュートによって制御しています。XMLのパース処理は比較的時間がかかるため、Pythonのバイトコードにコンパイルして動作を高速化しているものもあります。下の例はGenshiのテンプレートです。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/">
<head>
<title>$title</title>
</head>
<body class="index">
<div id="header">
<h1>$title</h1>
</div>
<p>Welcome!</p>
<div id="body" py:content="body">
Here is body elemenet
</div>
</body>
</html>
もう一つは、XMLをベースとしないテンプレートエンジンです。makoやcheetahが有名です。特にMakoは他のテンプレートエンジンに比べて動作速度を売りにしていますが、グラフィカルなHTMLエディタとの親和性を犠牲にしています。また、Makoはバージョン0.2ととても若いプロジェクトですが、python.orgのサイトにも使用されるなどの実績もあります。また、後述するPylonsの標準テンプレートエンジンに採用されています。下の例はMakoのテンプレートです。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${title | h}</title>
</head>
<body class="index">
<div id="header">
<h1>${title | h}</h1>
</div>
<p>Welcome!</p>
<div id="body">
${body}
</div>
</body>
</html>
どのテンプレートエンジンにも一長一短がありますが、基本的な機能はすべて揃っています。シビアな動作速度が要求される環境ではMakoをお薦めしますが、通常の環境では実際のテンプレートを眺めて自分の気に入ったものや、フレームワークのデフォルトのテンプレートを使うのがよいでしょう。筆者のお気に入りのテンプレートエンジンはGenshiですが、ここではMakoを説明します。
Makoテンプレートエンジン
早速インストールしてみましょう。
> easy_install Mako
インストールが終了後、Pythonの対話モードを立ち上げてください。実験のお時間です。
まず、対話モードで次のように入力してください。このコードは、render(テンプレートの文字列, テンプレートの引数)と入力すれば、テンプレートの文字列をMakoのテンプレートとして評価して結果を標準出力に表示します。
>>> from mako.template import Template
>>> def render(template, mystring=""):
... print Template(template).render(mystring=mystring)
...
>>>
それでは、動作確認してみましょう。下の例は、「This is my first template」を出力するコードです。この例ではkwdsに何も指定していません。
>>> render("This is my first template")
This is my first template
>>>
値の表示
では、mystringに値を設定して、テンプレートでその値を出力します。renderで指定した変数mystringには、テンプレートのどこからでもアクセスできます。アクセスするには,${変数名}で出力します。ここでは,${mystring}になります。${から}の間に変数名でなくPythonのコードを書けば,コードを評価した結果が出力されます。
>>> render("This is my ${mystring} template", mystring="first")
This is my first tempalte
>>>
WebアプリケーションではHTMLで出力することが多いですが、mystringにHTMLタグが含まれている場合はどうなるでしょうか?
>>> render("This is ${mystring} template", mystring="<b>first</b>")
This is <b>first</b> template
>>>
HTMLタグがそのまま出力されます。出力時に文字列をエスケープしたいケースがあります。文字列をエスケープするには、次の形式で記述します。
${変数名 | エスケープ方法}
エスケープ方法には次の形式があります。
|識別子 | エスケープ方法
|u | URLエンコード
|h | html エスケープ
|x | xmlエスケープ
さて、上の例でHTMLでエスケープしてみます。
>>> render("This is ${mystring | h} template", mystring="<b>first</b>")
This is <b>first</b> template
>>>
制御構造
Makoでの制御構造(if文やfor文など)は%で始めます。%の後に続けて、Pythonの制御構造と同じ形式で記述します。Pythonのブロック構造はインデントで表していましたが、Makoではブロックの終わりは「% end制御キーワード」のように記述します。制御キーワードはifやforなどです。
条件分岐
if文による条件分岐は次のように記述します
% if mystring == "first":
1st
% endif
条件分岐ではPythonにあるelifやelseももちろんサポートしています。
% if mystring == "first":
1st
% elif mystring == "second":
2nd
% else:
${mystring}
% endif
繰り返し処理
for文での繰り返しも同様に記述できます。
% for i in range(0, 10):
${i}
% endfor
Makoでは制御構造をPythonの制御構造にとても似た形で記述できます。Makoに限らず、他のテンプレートエンジンでもPythonらしく記述することを指向しているものが多いように感じます。
便利な機能
コメント
テンプレートの中にコメント行を埋め込むことができます。このコメント行は、レンダリング時に無視されて出力されません。コメントは行の先頭を「##」で始めます。
## This is comment line.
This is output line.
改行の制御
テンプレート内での改行は、出力時に改行コードがそのまま出力されます。HTMLのように改行自体に意味を持たないフォーマットでは問題になることはありません。メールなどのプレーンテキストで出力する場合は、改行コードを入れたくない場合があります。改行コードを入れずに行が続いていることを示すために、行の最後に「¥」を入力します。
この行は次の行に ¥
続きます
Pythonコードの埋め込み
賛否両論ありますが、Pythonのコードをテンプレート中に埋め込むことができます。多用しすぎるとMVCの構造が破綻する可能性もあるので気をつけてください。
Pythonコードブロックは「<%!」で始まり、「%>」までの間に記述します。Pythonコードブロック中ではPythonのすべての機能を使うことができます。ここで定義された変数は、テンプレートの他の場所からもアクセスできます。
<%!
import re
def filter_dot_string(text):
return re.sub("^¥.*", "", text)
%>
<%!
r = filter_dot_string(".. text ..")
%>
${r}
もっと高度な機能
開発が進み、テンプレートの量が増えてくると、重複したコードが多くなります。また、ページ間で共通の外観を提供する必要があるかもしれません。Makoでは、複数のコードの部品化の仕組みが提供されています。
外部ファイルの取り込み
includeタグを使うことで、外部のファイルをテンプレートに埋め込むことができます。<%includeではじまり、HTMLタグのような記述になります。fileアトリビュートで取り込みたいファイルを指定します。複数のテンプレートにまたがって、共通の要素を埋め込む場合に簡単に利用できます。
<%include file="some.html">
This is original text.
<%include file="other.hmlt">
関数による部品化
includeタグでは、外部ファイルのコンテンツを埋め込むだけですが、関数による部品化では引数に応じて処理を変更することができます。関数は次のように<%def name="関数名(引数)">と記述します。同じテンプレート内で何度も処理を呼び出す場合に便利です。
<%def name="hello(gender, who)">
<tr><td>${who}</td><td>${gender}</td></tr>
</%def>
<table>
%for gender, who in data:
${hello(gender, who)}
%endfor
継承
ここでは紹介だけにとどめますが、テンプレートを継承して別のテンプレートを作成することができます。共通の外観を持った複数の種類のページを作る時に便利な機能です。
Makoには他にも便利な機能がたくさんあります。また、今回はMakoについて紹介しましたが、Genshiや他のテンプレートエンジンにも同様の機能が提供されています。Pythonではいくつかの選択肢が用意されているので、自分の好みにあったものを使うのがいいでしょう。