Page Template ( ZPT ) の 高度な使い方
この文書は、Zope Book 最新版の "Advanced Page Templates" を日本語に翻訳したものです。Page Template ( ZPT ) の仕様の詳細や、多くのサンプルコードを含んだ、大変よくまとめられたドキュメントです。
この文書について
この文書は、Zope Book 最新版の "Advanced Page Templates" を日本語に翻訳したものです。Page Templatesについてより深く学びたい技術者、デザイナー向けの文書です。Page Templatesの仕様の詳細や、多くのサンプルコードを含んだ、大変よくまとめられたドキュメントです。 なお、原文は以下のURLにあります。
http://www.plope.com/Books/2_7Edition/AdvZPT.stx
Page Templates の 高度な使い方
「Zope Page Templates を使う」 という章では Page Templates の基本的な使い方について学びました。 この章では、新しい「式」の使い方を含め、 より高度な利用方法について学びます。
TAL の高度な使い方
この章では、すべての TAL 式とオプションを紹介します。付録Cの「Zope Page Templates リファレンス」では、より簡潔に TAL 式について網羅しています。
この章では、'タグ' および 'エレメント' という単語は " XHTML の仕様:http://www.w3.org/TR/2000/REC-xhtml1-20000126/#defs に基づいた意味で使用しています。 "<p>" は *タグ*で、一方,"<p>?</p>" のように「終了タグ」と「開始タグ」で囲まれたブロックすべてを「エレメント」と呼びます。
コンテンツの高度な挿入方法
「Zope Page Templates を使う」という章で、tal:content や tal:replace といった
TAL 文がどのように動作するかについて見てきました。この章ではコンテンツを挿入するためのより高度な手法について学びます。
HTMLを挿入する
通常、tal:replace や tal:content といった TAL 文では、文字列中に現れる
HTML のタグやエンティティを 「エスケープして」埋め込み、一般的な文字列と
して見えるように処理します。HTML として解釈されるようなエレメントとしては出
力しません。
たとえば、'<' という文字は「エスケープ」されて &lt; となって埋め込まれます。
文字列中にある HTML の構文をエスケープせずにそのまま埋め込みたい場合には、
'structure'というキーワードを利用する必要があります。
この機能は、 HTML や XML の部品を挿入する場合に便利です。 HTML や XML の部 品は、文字列として保存されていることもあるでしょうし、他のZopeオブジェクト から出力されることもあるでしょう。 たとえば、一部の文字列を太字や斜体にする、といった簡単な HTML を含んだ ニュース記事があるとしましょう。 この場合、HTML をそのまま、「トップニュース」のページに埋め込みたいと思う でしょう。 HTML をそのまま挿入するには、以下のようにします:
<p tal:repeat="newsItem here/topNews"
tal:content="structure newsItem">
<code>HTML</code> を含んだニュース記事
</p>
こうすることによって、ニュース記事中の HTML はそのままパラグラフの中に挿入されます。
組み込み変数の here はテンプレートがレンダリングされているフォルダを指しています。
here に関するより詳しい解説は、"式" という項目をご覧ください。
この例では、Zope オブジェクト topNews を探す起点として here を利用しています。
topNews は、もしかしたらニュース記事のリストである場合もあるでしょうし、リストを取得するためのスクリプトかもしれません。
structure というキーワードを追加すると newsItem にある文字列をエスケープしなくなります。
実際に HTML のタグが含まれているかは問題ではありません。structure は「文字列をそのまま出力」し ます。
この機能はデフォルトにはなっていません。なぜなら、テンプレートに挿入される文字列は多くの場合 HTML を含んでいないことが多いからです。
しかし、HTML を含む「ページ構造」を挿入したい場合は、デフォルトの動作は妨げとなるでしょう。
ダミーのエレメント
組み込みの変数 nothing を使うと、テンプレート上にのみ存在し実際には出力されないページのエレメントを埋め込むことができます。
次のようにします:
<tr tal:replace="nothing">
<td>10213</td><td>ダミーのエレメント</td><td>$15.34</td>
</tr>
この手法は、動的に埋められるページの一部に「ダミー要素」を含めるために利用すると便利でしょう。 たとえば、展開すると10列ほどになるテーブルが、テンプレート上では1列にしか見えない場合などが相当するでしょうか。 ダミーの列を9つ挿入することで、テンプレートはより実際の出力結果に近づくはずです。
デフォルトのコンテンツ
tal:content あるいは tal:replace という TAL 式と一緒に default を使うと、エレメントの内容(コンテンツ)をそのまま保持することができます。
以下が例です:
<p tal:content="default">Spam</p>
これは、以下のように出力されます:
<p>Spam</p>
実際には、デフォルトのコンテンツを常に残すのではなく、残すかどうかを選ぶ場合がほとんどでしょう。 以下のようにしてください:
<p tal:content="python:here.getFood() or default">Spam</p>
注意: Python 式についてはこの章の後で解説します。
getFood という関数が「真」と判断される値(つまり空でない文字列など)を返した場合、タグの中に関数が返した値が挿入されます。
そうでなければ、デフォルトの " Spam " という文字列が挿入されます。
Advanced Repetition
高度な繰り返し
tal:repeat という文の働きについては、「 Zope Page Templates を使う」という章で解説しました。
ここでは、tal:repeat 文の、より高度な使い方について解説します。
変数を繰り返す
ここで詳しく解説するのは「繰り返し変数」についてです。 繰り返し変数はループに関するさまざまな情報を提供します。 以下に上げたアトリビュートは「繰り返し変数」上で利用できます。 :
- index - ゼロから始まる繰り返し数(インデックス)
- number - 1から始まる繰り返し数(インデックス)
- even - ループが偶数回(0, 2, 4, ...)の場合に真となる
- odd - ループが奇数回(1, 3, 5, ...)の場合に真となる
- start - ループの最初で真となる
- end - ループの最後で真となる
- length - ループの長さ。繰り返す総数
- letter - ループの繰り替え指数を英小文字で返す: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", のように続く
- Letter - letterの大文字版
「繰り返し変数」にアクセスするには path 式、あるいは Python 式を使います。
path 式を使う場合、repeat という文字列に続けて変数名を記述し、その後上記にあるうちの得たい情報を示すアトリビュートの3つを記述します。
たとえば、'repeat/item/start'のようにします。
Python 式では、繰り返し変数を利用するために Python の辞書型(dictionary)で利用する表記法を利用します。
たとえば、'python:repeat['item'].start'のようにします。
tal:repeat 文が入れ子(ネスト)になった場合にも困らないように、repeat/start のように単純に表記せず、情報を引き出す対象を明記するようになっています。
繰り返しの「Tips」
ここでは2つの実践的な「Tips」を取り上げましょう。
テンプレートの一部を繰り返したいが、talを埋め込むのに適当な、繰り返し部分をうまく囲んでいるエレメントが見つからない場合があるかも知れません。
このような場合は、繰り返したい部分を囲むようにエレメントを追加しますが、出力されたページには追加したエレメントは現れて欲しくありません。
tal:omit-tag という文を使うことで、追加したエレメントを隠すことができます:
<div tal:repeat="section here/getSections"
tal:omit-tag="">
<h4 tal:content="section/title">タイトル</h4>
<p tal:content="section/text">引用</p>
</div>
出力結果から数文字を少なくするためにこのようなことを行うわけではありません。
特にスタイルシートを使っていた場合 div タグを出力してしまうとレイアウトに影響を与えます。
omit-tag という文を使うことで、div タグ(と、ペアとなる閉じタグ)を出力されないようにし、レイアウトに影響を与えないようにしています。
tal:omit-tag 文については、のちほどより詳しく解説します。
繰り返しになりますが、tal:repeat 文は「入れ子」にすることができます。
それぞれの tal:repeat 文は異なる繰り返し変数を持っています。
以下の例では、「かけ算」をテーブルに表示しています:
<table border="1">
<tr tal:repeat="x python:range(1, 13)">
<td tal:repeat="y python:range(1, 13)"
tal:content="python:'%d x %d = %d' % (x, y, x*y)">
X x Y = Z
</td>
</tr>
</table>
この例では Python 式を使っています。 Python 式については後ほど解説します。
DTMLの dtml-in によるループに親しんでいるなら、「バッチ」をご存じでしょう。
「バッチ」は長いリストを短いリストに分割する処理です。
Web ページにリストを表示する際、長いリストを分割して短く表示するために利用されることが多いようです。
検索エンジンの結果表示を思い浮かべてもいいでしょう。
tal:repeat 文は「バッチ」をサポートしていませんが、バッチ用のユーティリティが組み込まれています。
この後の「バッチ」という節をご覧ください。
'tal:repeat'がサポートしていない便利な機能がもう一つあります。それは「ソート機能」です。
リストをソートしたい場合は、ソート専用のスクリプトを作るか( Python でそのような関数を作るのは簡単です)、sequence.sort というユーティリティ関数を利用します。
以下は、オブジェクトのリストを「タイトル」と「更新日」の優先順位でソートする例です:
<table tal:define="objects here/objectValues;
sort_on python:(('title', 'nocase', 'asc'),
('bobobase_modification_time', 'cmp', 'desc'));
sorted_objects python:sequence.sort(objects, sort_on)">
<tr tal:repeat="item sorted_objects">
<td tal:content="item/title">タイトル</td>
<td tal:content="item/bobobase_modification_time">
更新日</td>
</tr>
</table>
この例では、理解しやすいように繰り返し変数をループの外で定義しています。
sequence.sort という関数は、ソートを行うリストと「ソート順に関する情報」を引数として渡します。
この例では、「ソート順に関する情報」は sort_on という変数に定義しています。
その他のsequence.sort の強力な機能については、「API リファレンス(未訳)」をご覧ください。
高度なアトリビュートコントロール
tal:attributes 文についてはすでに触れました。
'tal:attributes'を使うと、タグの「アトリビュート」を動的に置き換えることができます。
たとえば、a エレメントの href アトリビュートを置き換えるときに利用します。
セミコロン(;)を使って複数のアトリビュートを区切って記述すると、複数のアトリビュート
を置き換えることができます。
以下のコードは "href" と "class" アトリビュートを動的に生成します:
<a href="link"
tal:attributes="href here/getLink;
class here/getClass">リンク</a>
XML の名前空間を使ってアトリビュートを定義することもできます。以下のようにします:
<Description
dc:Creator="creator name"
tal:attributes="dc:Creator here/owner/getUserName">
概要</Description>
XML 名前空間のプリフィックスをアトリビュート名の前に置くと、XML 名前空間を置換できます。
変数の定義
tal:define を使えば変数を定義できます。
変数を宣言するにはいくつか理由があるでしょう。
テンプレートの中で長い表記を繰り返し記述することを避けるために変数を定義することがあるかもしれません。
あるいは、処理時間のかかる関数を繰り返し呼ぶ代わりに、一度関数を実行した結果を保存する目的で、変数を定義することもあるでしょう。
タグのエレメントの中で一度変数を定義すると、タグで囲まれたエレメントの中で何度でも利用できます。
以下の例では、変数を定義した後で「テスト」し、変数を使って繰り返しを行っています:
<ul tal:define="items container/objectIds"
tal:condition="items">
<li tal:repeat="item items">
<p tal:content="item">id</p>
</li>
</ul>
上の例では、tal:define で items という変数を定義しています。items 変数
は ul エレメント内であればどこででも利用できます。
ul タグの中に2つの TAL 文があることにも注意してください。
「TAL 文の相互作用」では、ひとつのタグ中で複数の TAL 文を使う場合についてより
詳しく解説します。
この例では、最初の文がitems という変数を定義し、次にある条件文(condition)
で item が偽( この場合は空のリストが偽となります )か真かを判別しています。
もし items 変数が偽であった場合は、ul エレメントは出力されません。
リストが空で何も出力されない場合に、メッセージを表示したいときにはどうすれば いいでしょうか。 リストの前に以下のようなコードを付け加えます:
<h4 tal:condition="not:container/objectIds">
表示する項目がありません</h4>
not:container/objectIds という式はcontainer/objectIds が偽の時に真となり、真の時に偽となります。
この式については「Not 式」という節でより詳しく解説します。
items 変数はまだ定義されていないため、ここで利用することはできません。
items の定義をh4 エレメントに移動すると、ul エレメントの中で利用できなくなってしまいます。
items はh4 エレメント内で定義されているローカル変数だからです。
h4 や ul を囲んでいるエレメントで変数を定義することもできますが、もっと簡単な解決方法があります。
global というキーワードを変数名の前に置くことで、span タグの外でも変数を利用できるようになります :
<span tal:define="global items container/objectIds"></span>
<h4 tal:condition="not:items">表示する項目がありません</h4>
セミコロン(';')を使って変数を区切ることで、tal:define で複数の変数を定義できます。
以下が例です :
<p tal:define="ids container/objectIds;
title container/title">
変数は好きなだけ定義することができます。
変数はそれぞれ「グローバル」か「ローカル」どちらかの「スコープ」を持っています。
同じ tal:define の中でより前に定義した変数を利用することもできます。
以下が例です:
<p tal:define="title template/title;
global untitled not:title;
tlen python:len(title);">
この例では、title と tlen を p タグ内のローカル変数として定義していますが、untitled はグローバル変数です。
tal:define を賢く使えば、テンプレートの効率が上がり、可読性も高くなります。
タグの省略
tal:omit-tag 文を使うと、タグを省略することができます。
このTAL 文が必要になることはあまりないかも知れませんが、時にはとても便利に利用できます。
omit-tag は開始タグと終了タグを除去しますが、タグに挟まれた部分やエレメントには影響を与えません。
以下が例です:
<b tal:omit-tag=""><i>このように</i> なります</b>
は次のように出力されます:
<i>このように</i> なります
この例のような使い方では、tal:omit-tag は tal:replace="default" とよく似た動作をします。
tal:omit-tag は真偽値を返す式と組み合わせて利用することができるので、式が真の場合にのみタグを除去できます。
以下が例です:
お友達: <span tal:repeat="friend friends">
<b tal:omit-tag="not:friend/best"
tal:content="friend/name">フレッド</b>
</span>
この例では友人のリストを生成していますが、「親友」は太字で表示されます。
エラー処理
Page Template 上でエラーが起こった場合、エラーを「キャッチ」してユーザに分かりやすいエラーメッセージを表示できます。 例えば、テンプレート上でフォームから渡されたデータを変数に定義するとしましょう:
...
<span tal:define="global prefs request/form/prefs"
tal:omit-tag="" />
...
Zope が prefs という変数が見つからない、といった問題に行き当たると、ページの出力全体が中断して代わりにエラー出力用のページを表示します。
tal:on-error というエラー処理用の文を使うことで、エラーページを出力しないようにすることができます。:
...
<span tal:define="global prefs here/scriptToGetPreferences"
tal:omit-tag=""
tal:on-error="string:エラーが発生しました">
...
テンプレートを出力中にエラーが発生すると、Zope はエラーを処理するため tal:on-error 文を探します。
Zope はまず、出力中のエレメントの中から tal:on-error を探し、次に出力中のエレメントを囲んでいるエレメントを、トップレベルのエレメントまで順に探します。
Zope が tal:on-error(エラーハンドラ) 文を見つけると、見つかったエラーハンドラを含むエレメントの中身を「式」の出力結果で置き換えます。
この例では、span エレメントにエラーメッセージが文字列として埋め込まれているので、この文字列を出力します。
たいていの場合、論理的なページのエレメントを囲むエレメントにエラーハンドラを定義することになるでしょう。 たとえば、テーブルを出力中にエラーが発生したとすると、エラーハンドラはテーブルを省略するか、なんらかのエラーメッセージで置き換えることでしょう。
より柔軟にエラー処理をするには、スクリプトを呼びます。 例えば:
<div tal:on-error="structure here/handleError">
...
</div>
div の中でエラーが起こると、handleError というスクリプトが呼び出されます。
structure というオプションがついているので、スクリプトが HTML を出力してもよい、というところに注目してください。
エラー処理用のスクリプトはエラーを調べ、エラーの種類によってさまざまな処理を行うことができます。
エラー処理用のスクリプトは名前空間( namespace )中にある error という変数を使ってエラーの内容を知ることができます。
以下が例です:
## Script (Python) "handleError"
##bind namespace=_
##
error=_['error']
if error.type==ZeroDivisionError:
return "<p>ゼロで割ることはできません</p>"
else:
return """<p>エラーが発生しました</p>
<p>エラーのタイプ: %s</p>
<p>エラー値 : %s</p>""" % (error.type,
error.value)
エラー処理用のスクリプトでは、メールを送るといったさまざまな処理を行うことができます。
tal:on-error 文は一般的な「例外処理」を意図したものではありません。
例えば、tal:on-error 文の中で、エラーが起こった際にフォーム入力を正しく変更するような処理はすべきではありません。
そのようなことをしたい場合には、スクリプトを使います。スクリプトを使えば強力な例外処理が可能です。
tal:on-error 文はテンプレートを出力中に起こる例外的な問題に対処するために利用してください。
TAL 文の相互作用
エレメントにひとつの TAL 文がある場合は実行の順番は単純です。 ルートのエレメントから初めて子供のエレメントを探索し、順に TAL 文を実行して行きます。
TAL 文はひとつのエレメントに複数記述できます。
ひとつのエレメントには、さまざまな組み合わせで TAL 文を記述できます。
ただし、tal:content と tal:replace を同じエレメントに記述することはできません。
エレメントの中に複数の TAL 文がある場合、以下のような順番で実行します :
- define
- condition
- repeat
- content or replace
- attributes
- omit-tag
tal:on-error 文はエラーが起こった場合にのみ実行されるため、リストには上がっていません。
このような実行順になっているのは以下のような理由があります:
定義した変数は他の文で利用することがあるでしょう。そのため、define の優先順位が高くなっています。
次に実行すべきことは、エレメント自体が評価されるべきかどうかを決めることです。そのため、次に condition を実行します。また、 condition は直前に定義した変数を利用することがあるので、 define の後に実行します。
ループを使ってエレメント内の部品を繰り返し出力するためには変数を利用します。このため、 repeat を次に実行し、content や replace 、 attributes と続けて実行するようになっています。
content と replace は同じ部分に影響を与えるため、単一のエレメントで同居できません。
omit-tag は他の TAL 文と関連性がなく、 define や repeat の後に実行されるべきであるため、一番最後に実行します。
以下が、複数の TAL 文を含んだエレメントの例です:
<p tal:define="x /root/a/long/path/x | nothing"
tal:condition="x"
tal:content="x/txt"
tal:attributes="class x/class">テキストの例</p>
tal:define 文が最初に実行され、その実行結果を他の文で利用していることに注意してください。
TAL 文を組み合わせて使う場合、以下の3つの制限に注意してください:
- それぞれの TAL 文は、同一エレメントで一回だけ利用できます。
HTML の仕様上、同一のアトリビュート名を複数記述できないようになっているためです。
例えば、
tal:define文を同一エレメントに2回記述することはできません。 -
tal:contentとtal:replaceを同一エレメント中で利用することはできません。機能的に衝突を起こすからです。 - TAL 文を記述した順番は実行順に影響を与えません。 記述する順番を変えたとしても、TAL 文はすでに見たような「固定の」順番で実行されます。
TAL 文の実行順を変えたい場合には、上位のエレメントを作って、先に実行したい文を記述します。 例えば、ループを使ってリストを生成する際、特定の条件を持った要素だけ出力したくない場合を考えましょう。 数値を 0 から 9 まで繰り返し、3だけ「スキップ」するようなコードを書こうと思って、以下のようなコードを書いたとします:
<!-- 動かないテンプレート -->
<ul>
<li tal:repeat="n python:range(10)"
tal:condition="python:n != 3"
tal:content="n">
1
</li>
</ul>
TAL 文の実行順の影響で、このテンプレートは期待通りに動作しません。
TAL 文の記述順に関係なく、condition は常に repeat 文の前に実行されます。
結果として n という変数はリストが生成されるまで定義されず、condition で評価するときには見つからないためエラーを起こします。
この問題を解決するには以下のようにします:
<ul>
<div tal:repeat="n python:range(10)"
tal:omit-tag="">
<li tal:condition="python:n != 3"
tal:content="n">
1
</li>
</div>
</ul>
このテンプレートでは、上位に div エレメントを置いて変数 n を定義することで問題を解決しています。
tal:omit-tag 文があるため、 div タグは出力されないことに注意してください。
このような場合 span や div を使うのは自然なことでしょう。XML には相当するエレメントが存在しません。
あるいは、 TAL 名前空間を利用することもできます。TAL はタグを定義せず、出力もされません。
TAL 名前空間の中ではあらゆるタグ名を利用することができるので、エレメントとして利用できます。
以下が例です:
<tal:series define="items here/getItems">
<tal:items repeat="item items">
<tal:parts repeat="part item">
<part tal:content="part">Part</part>
</tal:parts>
</tal:items>
<noparts tal:condition="not:items" />
</tal:series>
この例にある tal:series や tal:items 、そして tal:parts タグは XML 名前空間をサポートするツールや HTML エディタで、恐らく正しく取り扱われるはずです。
この手法は 'div'を使うのに比べ2つの利点を持っています。
まず、TAL タグ TAL 属性と同じように出力に現れません。このため、tal:omit-tag は不要です。
また、TAL 属性はタグの名前空間を継承しているため tal: 型プリフィックスを要求しません。
同じ方法を使って METAL 名前空間を利用することもできます。
フォームの処理
DTML では、「フォームとアクションのペア」と呼ばれる手法でフォームを処理することができます。 「フォームとアクションのペア」は2つの DTML を利用します。 ひとつの DTML ではフォームを表示しユーザが入力するデータを収集します。 もうひとつの DTML では、入力を受けて処理をし、ユーザに応答を返します。 「フォーム」が「アクション」を呼ぶわけです。 「フォームとアクションのペア」については「DTML による動的コンテンツ(未訳)」をご覧ください。
Zope Page Templates では「フォームとアクションのペア」の手法はうまく活用できません。 というのは、この手法ではフォームのデータ処理と応答の出力を同じオブジェクト(アクション)で実行することを前提としているからです。 Page Templatesでは、「フォームとアクションのペア」のかわりに「フォーム、アクション、レスポンス(応答)」という手法を利用します。 「フォーム」と「レスポンス」は Page Templates となり、「アクション」はスクリプトとなるでしょう。 「フォーム」のテンプレートがユーザーが入力する情報を収集し、「アクション」のスクリプトを呼び出します。 「アクション」のスクリプトは入力を処理して、「レスポンス」のテンプレートを返します。 この手法は「フォームとアクションのペア」に比べより柔軟です。なぜならスクリプトが様々な種類のオブジェクトを応答として返すことができるからです。
以下は「フォーム」に相当するテンプレートの例です:
...
<form action="action">
<input type="text" name="name">
<input type="text" name="age:int">
<input type="submit">
</form>
...
このフォームに入力されたデータは、次のスクリプトで処理されます :
## Script (Python) "action"
##parameters=name, age
##
container.addPerson(name, age)
return container.responseTemplate()
このスクリプトはフォームに入力されたデータを処理し、別のテンプレートを 「レスポンス」として返します。 スクリプトから呼び出すことで、Page Templateを出力できます。 「レスポンス」のテンプレートは、フォームのデータが正しく処理されたというメッ セージを含むはずです。
「アクション」のスクリプトでは様々な処理を実行できます。 入力を判定したり、エラー処理をしたり、メールを送るなど、「処理を終えるために 必要な」様々な処理を実行できます。 以下が入力判定をするスクリプトの例です :
## Script (Python) "action"
##
if not context.validateData(request):
# フォームの Page Template から送られてきたデータに問題がある場合
# エラーメッセージを引数に渡してフォームテンプレートを呼びます
return context.formTemplate(error_message='Invalid data')
# 問題がなければ結果のページを表示します
return context.responseTemplate()
このスクリプトでは、フォームからの入力をチェックし、問題がある場合はエラーメッセージをともなってフォームテンプレートを再表示します。
スクリプトの context 変数は TALESの here と同じです。
「キーワード引数」を使って Page Templates に追加の情報を渡すこともできます。
options という組み込み変数を使うと、キーワード引数をテンプレート上で利用できます。
フォームテンプレートでは、以下のようにしてエラーメッセージを埋め込みます:
<span tal:condition="options/error_message | nothing">
エラー: <b tal:content="options/error_message">
エラーメッセージをここに表示します。
</b></span>
この例ではキーワード引数を使って渡されたエラーメッセージを表示する方法を紹介しました。
| nothing を使って error_message という引数がなかった場合にはエレメントを出力しないようになっているところに注目してください。
アプリケーションによっては、レスポンスとしてスクリプトから直接テンプレートを返すのではなく、Page Template にリダイレクトしたいことがあるかも知れません。 リダイレクトをするとネットワークは2回処理を行うことになりますが、Webブラウザに表示される URL を変更することができます。場合によってはスクリプトから直接テンプレートを返すより便利かも知れません。
もし手早くフォームを作りたいときは、単一の Page Template を使って「フォームとアクションのペア」を作ることができます。
ただし、エラー処理のことを考えなくてよく、ユーザーがどんな入力をしてもレスポンスがつねに一緒である場合にこの方法を利用してください。
Page Templates では dtml-call に相当する処理ができません。このため、入力を処理するスクリプトが返す結果を表示しないように関数呼び出しを行うよう、ちょっとした「ハック」を活用してください。
以下が例です:
<span tal:define="unused here/processInputs"
tal:omit-tag=""/>
この例では processInputs という関数を使い、 unused という利用されない変数に結果を代入しています。
式
Page Template の「式」についてはすでに解説しました。
「式」は「文」の値を提供します。
例えば <td tal:content="request/form/age">Age</td> という TAL 文では、「式」は request/form/age の部分に相当します。
request/form/age は *Path 式* の一種です。
Path 式にはrequest/form/age や user/getUserName といった「パス(Path)」を渡して対象となるオブジェクトを表記します。
式は TAL 文の内部でのみ動作します。一般の HTML に記述しても動作しません。
この節では、様々なタイプの「式」や「変数」について解説します。
組み込みの Page Template 変数
「変数」は「式」の中に記述することができます。
template 、 user 、 repeat や request といった組み込みの変数に関して
はすで触れました。
ここでは組み込み変数と利用方法の完全なリストを提示します。
ここで取り上げる組み込み変数は Script (Python) (Python スクリプト)の組み込み
変数とは異なります。Page Template でのみ有効です::
-
nothing - 空の文字列のように「偽」を返す値です。
tal:replaceやtal:contentでエレメントやエレメントで囲まれた部分を消去するために利用できます。 アトリビュート部分にnothingを記述すると、アトリビュートをタグから削除します(または出力しない)。 空の文字列を使った場合は、'alt=""'のように値のないアトリビュートを挿入します。 -
default - 出力に変更を与えない働きを持つ特別な変数です。
tal:replace、tal:contentやtal:attributesで利用します。タグで囲まれた部分に記入されているダミーのテキストをそのまま残します。 -
options - テンプレートに渡されたキーワード変数です。
テンプレートが直接出力される際は、
optionsは存在しません。optionsはテンプレートがスクリプトか同種のものから出力された場合にのみ利用できます。 たとえば、tというテンプレートがt(foo=1)という Python 式で呼ばれた場合、options/fooは1になります。 -
attrs - TAL 式が存在するタグのアトリビュートを含んだ辞書です。 辞書のキーとなるのはアトリビュートの名前で、キーに対応する値はテンプレート上に埋め込まれたオリジナルの値です。 まれに必要な場合があります。
-
root - Zope のルートオブジェクトです。 Zope 上の固定の位置にあるオブジェクトを取得する際に利用してください。テンプレートの設置場所、あるいは呼ばれている場所にかかわらず同じようにオブジェクトを取得することがてきます。
-
here - テンプレートが呼ばれているオブジェクトを指します。
たいていの場合 container と同じですが、Zope の「獲得」を使っている場合には
containerとhereは異なります。 テンプレートが呼び出されている場所を基準に、Zope 上のオブジェクトを探し出して取得したい場合に利用してください。hereは Python スクリプトのcontextに似た変数です。 -
container - コンテナです。たいていはテンプレートが設置されている「フォルダ」になります。
テンプレートが設置されている場所を基準に、 Zope 上のオブジェクトを取得したい場合に利用してください。
containerとhereの二つの変数は、テンプレートが設置場所で呼ばれている場合は同じ値になります。 しかし、テンプレートが他のオブジェクトから呼ばれている場合、containerとhereは同じオブジェクトを指しません。 -
modules - テンプレート上で利用できる Python のモジュールを集めた組み込み変数です。 詳しくは「Python 式を書く」という節をご覧ください。
組み込み変数の実例については、この章を通じて取り扱います。
String 式
String 式を使うと文字列と Path 式を容易に混在させることができます。
string: に続く全ての文字列が評価され、Path 式として埋め込まれた文字列を検索します。
それぞれの Path 式はドル記号($)に続けて記述します。
以下が例です:
"string:ただの文字列です。Path 式はありません"
"string:copyright $year by フレッド・フリントストーン."
Path 式にひとつ以上のパートを含む(スラッシュがある)場合、あるいは空白を含まずに続く文字列と区切りたい場合には、大括弧({})で囲みます。
以下が例です:
"string:${vegetable}s を 3 つください。"
"string:あなたの名前は ${user/getUserName}ですね!"
vegetable というパスを大括弧で囲んでいるので、 Zope は vegetables という名前を参照しに行かないということに注意してください。
テキストはアトリビュートの中に記述する必要があるので、二重引用符('"')を埋め
込みたい場合には " のように実体参照を使って記述する必要があります。
また、ドル記号 ($) は Path 式の前に補われる記号として利用されるため、
ドル記号自体を埋め込みたい場合には二回繰り返します($$)。
以下が例です:
"string:以下の金額をお支払いください: $$$dollars_owed"
"string:"Hello world." と彼女は言った。"
文字列を検索置換する、または英子文字を大文字にするといった複雑な文字列操作は、String 式を使って簡易に実現することはできません。 そのような処理をしたい場合には、Python 式を使うか、スクリプトを利用してください。
Path 式
Path 式は URL に似た「パス」を使って参照するオブジェクトを記述します。 パスをオブジェクトの階層を表記しています。 パスは組み込み変数、繰り返し変数、またはユーザが定義した変数など、定義済みの変数から始まり、目的のオブジェクトに到達します。 以下が Path 式の例です:
template/title
container/files/objectValues
user/getUserName
container/master.html/macros/header
request/form/address
root/standard_look_and_feel.html
Path 式では、あるオブジェクトから、プロパティや関数などの子供のオブジェクトへと渡って行きます。 Path 式では「獲得」を利用することもできます。 獲得についてより詳しいことについては「高度な Zope スクリプティング(未訳)」という章の「Web からスクリプトを呼ぶ」という節をご覧ください。
Zope は Path 式によるオブジェクトの獲得を、 URL 上の獲得と同じように制限します。 Path 式でオブジェクトを参照するためには、そのオブジェクトにアクセスするために適切な「権限」を持っている必要があります。 オブジェクトの権限について詳しいことは「セキュリティ(未訳)」という章の「ユーザとセキュリティ」という節をご覧ください。
代替のパス
template/title というパスはテンプレートが利用される際には必ず存在していると保証されています(空の文字列であるかもしれません)。
request/form/x のようなパスは、場合によっては存在しないかも知れません。
存在しない場合は、Zope は Path 式を評価する際にエラーを起こします。
パスが存在しない場合は、代替のパスや代わりに利用する値を設定したいと思うことがあるかも知れません。
例えば、request/form/x が存在しない場合は、 here/x を代わりに利用する、といったことができればよいわけです。
パスを参照する順番に | で区切って並べれば、代替のパスを設定できます:
<h4 tal:content="request/form/x | here/x">ヘッダ</h4>
nothing と default は代替のパスの最後に記述すると便利な組み込み変数です。
例えば、default を使うと tal:content はダミーとして埋め込まれた文字列
を残したまま出力します。
TAL 文によって default と nothing は解釈の仕方が異なります。
付録Cの 「Zope Page Template リファレンス」をご覧ください。
Path 式でない TAL 文を複数のパスの最後に利用することもできます。 以下が例です:
<p tal:content="request/form/age|python:18">年齢</p>
上の例では、request/form/age というパスが存在しない場合は、'18'という数値が出力されます。
この形式を使うとパスとして表現できない値をデフォルトの値として利用することができます。
Path 式「でない」文を利用できるのは、一番最後だけであるということに注意してください。
以下のような表記方法もあります :
<span tal:content="here/?myvar" />
here/?myvar という式は here オブジェクトにある、 myver という変数の
文字列に相当するアトリビュートを評価します。
たとえば、myvar に title という文字列が代入されていたとすると、 here/?myvar は title/here と同じ値を返すことになります。
exists という式プリフィクスを使うと、パスが存在するかどうかを調べることができます。 詳しくは"Exists 式" という節をご覧ください。
Not 式
Not 式は値の否定値を返します。以下が例です:
<p tal:condition="not:here/objectIds">
オブジェクトがありません。
</p>
Not 式は与えられた式が偽の場合に真となります。逆に真の場合は偽となります。
ゼロ、空の文字列、空のリストや nothing 、および None は偽として評価されます。その他の値は真となります。
存在しないバスは真でも偽でもありませんので、not 式に存在しないパスを与えるとエラーとなります。
Python 式では Pythonの not キーワードが利用できるため、Not 式を利用する意味はあまりないでしょう。
Nocall 式
通常の Path 式はオブジェクトを取得してから出力します。 このため、オブジェクトがスクリプトや関数のように何かを実行するオブジェクトだった場合、オブジェクトを呼び出して評価します。 たいていはこのような動作で問題ありませんが、たとえばプロパティを取得するために DTML Document を変数に代入する場合は都合がよくありません。 DTML Document 自体が評価されて文字列に変換されるため、DTML Documentのオブジェクト自体を取得できないからです。
nocall: という型プリフィクスをパスの前に追加すると、文字列への変換が抑止されてオブジェクト自体を返します。
以下が例です:
<span tal:define="doc nocall:here/aDoc"
tal:content="string:${doc/getId}: ${doc/title}">
Id: Title</span>
この型プリフィクスは、Python 式で関数やモジュールオブジェクト自体を変数に保存したい場合にも有用です。
Nocall 式はオブジェクトに対してだけでなく、関数を対象にしても利用できます:
<p tal:define="join nocall:modules/string/join">
この式は関数(string.join)を呼び出した結果ではなく、関数自体を定義して join 変数に格納します。
Exists 式
パスが存在すればexist式は真になり、存在しなければ偽になります。 たとえば、リクエストにエラーメッセージが渡された場合に、表示する にはこのようにします :
<h4 tal:define="err request/form/errmsg | nothing"
tal:condition="err"
tal:content="err">エラー!</h4>
exist式を使うともっと簡単にできます :
<h4 tal:condition="exists:request/form/errmsg"
tal:content="request/form/errmsg">エラー!</h4>
exist式はnot式と一緒に使うことができます :
<p tal:condition="not:exists:request/form/number">0から5までの
数字を入力してください</p>
この例では、"not:request/form/number"というような式を使うことは
できません。この式だと number 変数が存在して、ゼロの場合に真と
なるからです。
Python 式
プログラミング言語Pythonはシンプルで表現力のある言語です。 Pythonを知らないのであれば、"PyJUGのWebサイト":http://www.python.jp/Zope にある "チュートリアル":http://www.python.jp/pub/doc_jp/tut/ を読んでみましょう。
Page Template では、Pythonで式とみなされているものすべてが
「Python式」として使えます。if や while といった式は使えま
せん。また、Zopeは、保護された情報にアクセスしたり、機密データを
変更したり、無限ループを作成するといったことができないように、セキュリティ上の制限をかけています。
Python のセキュリティ上の制限についての詳細は、「Advanced Zope
Scripting(未訳)」という章をご覧ください。
比較
Python式がどうしても必要になるのは 'tal:condition'式においてです。
文字列や数字を比較する際に、TALではPython式を使います。
Python式では比較演算子 '<' (より小さい)、 '>' (より大きい)、
'==' (等しい), '!=' (等しくない)が使えます。
ブール演算子、 and 、 not 、 or も使えます。たとえば :
<p tal:repeat="widget widgets">
<span tal:condition="python:widget.type == 'gear'">
ギア #<span tal:replace="repeat/widget/number>1</span>:
<span tal:replace="widget/name">名前</span>
</span>
</p>
この例では、オブジェクトを全体を走査し、 gear 型ウィジェットの
情報を表示します。
単一の式にひとつ以上の条件を設定して、異なる値を振り分けたいことがあります。
こういうときには、次のようにして test 関数を使
います :
You <span tal:define="name user/getUserName"
tal:replace="python:test(name=='Anonymous User',
'ログインしてください', default)">
以下の名前でログインしています:
<span tal:replace="name">Name</span>
</span>
ユーザが Anonymous なら span エレメントは 'ログインしてください'
というテキストに置き換わり、そうでないなら、デフォルトのコンテ
ントが使われ、この場合だと、'以下の名前でログインしています...' になります。
test 関数は if/then/else 式のように機能します。
test 関数の詳細は、付録 A の 「DTML リファレンス(未訳)」をご覧ください。
test 関数を使った別の例です :
<tr tal:define="oddrow repeat/item/odd"
tal:attributes="class python:test(oddrow, 'oddclass',
'evenclass')">
これはテーブルの各行に oddclass と evenclass 属性を割り振って
いて、HTMLで表示すると偶数行と奇数行が別のスタイルで表示されるといった処理を実現しています。
test 関数を使わなければ、偶数の行、奇数の行と条件によって、
tr エレメントを2つ書かなければ生けません。
他の式を使う
Python式には利用可能な式の型が他にもあります。各式のタイプは、
path() 、 string() 、 exists() 、 nocall() というように、タイプ
の名前に対応する関数があります。これらの関数を使うと、次のよ
うな式を書くことができます :
"python:path('here/%s/thing' % foldername)"
"python:path(string('here/$foldername/thing'))"
"python:path('request/form/x') or default"
最後の例は "request/form/x | default" というPath式とは少し異なった意味を持っています。 "request/form/x" が存在しない、あるいは偽と評価されるとデフォルトのテキストが使われるからです。
Zope オブジェクトの取得
様々な機能を持った「オブジェクト」を組み合わせて利用できる、ということは Zope が持っている強みのひとつです。 Page Template はScript、SQL メソッド、カタログ、カスタマイズ したコンテント・オブジェクトと組み合わせて利用できます。 これらのオブジェクトを使うためには、Page Template 内でオブジェクトにアクセスする方法を知っていなければなりません。
オブジェクトのプロパティはたいてい「属性」なので、"template.title" という式で
テンプレートのタイトルを取得できます。
Zope の多くのオブジェクトは獲得に対応しているため、"親"オブジェクトにある属性を取得することもできます。
したがって、Python 式 "here.Control_Panel" はルートフォルダにあるコントロールパネルを取得します。
オブジェクトのメソッドも "here.objectIds"や"request.set"というように「属性」としてアクセスできます。
フォルダに入っているオブジェクトはフォルダの属性を使ってアクセスできますが、
Pythonの識別子として適当でないIDを持っていることがよくあるので、
普通の表記は使えません。
たとえば、penguin.gif という名前のオブジェクトに、次のような Python
式でアクセスすることはできません :
"python:here.penguin.gif"
かわりに、このようにアクセスします :
"python:getattr(here, 'penguin.gif')"
Pythonはピリオドを含んだ属性名に対応していないからです。
request 、module といったオブジェクトやZopeフォルダはアイテ
ム・アクセスに対応しています。例えば :
request['URL']
modules['math']
here['thing']
フォルダでアイテム・アクセスを使うと、名前を獲得しようとはしな いので、フォルダに同じ名前のオブジェクトが実際に存在する場合にのみオブジェクトを取得できます。
前の章で示したように、Path式を使えば、あるオブジェクトから次のオブジェクト をどうやって取得するのかについて、細かいことを気にしなくてすみます。Zopeは まずアトリビュートでアクセスし、次にアイテムでアクセスします。 :
"python:getattr(here.images, 'penguin.gif')"
の代わりに次のように書きます:
"here/images/penguin.gif"
また :
"python:request.form['x']"
の代わりに次のように書きます:
"request/form/x"
Path式は便利ですが、Path式では細部を決めることができないという欠点も持っています。 たとえば、フォームに"get"という名前の変数があったとすると、このように 書かなければいけません。 :
"python:request.form['get']"
次のようなPath式 :
"request/form/get"
はフォームの辞書の "get" メソッドと評価されるため、期待したとおりに動作しません。
上で述べたように、 path() 関数を使えば Python式の中でPath式を使うことも
できます。
スクリプトを使う
スクリプト・オブジェクトはビジネスロジックと複雑なデータの操作 をカプセル化するのによく使われます。 複雑な式を使ったTAL式をたくさん書いているのに気がついたら、スクリプト でもっとすっきり書けないか検討してみるとよいでしょう。 テンプレートの文や式で使われているTALが理解に手間取るなら、 Page Template をシンプルにして、複雑な箇所はスクリプトを使うと よいでしょう。
各スクリプトには、呼ばれたときに与えられるパラメータのリストが あります。このリストがからであれば、Path式を書いてスクリプトを呼び出せます。 スクリプトに引数を与えるためには、Python式を使う必要 があります。たとえば :
"python:here.myscript(1, 2)"
"python:here.myscript('arg', foo=request.form['x'])"
スクリプトから Page Template に複数のデータを返したい場合は、辞書にして返すとよいでしょう。
スクリプトから返された辞書には、すべてのデータを保持する変数を定義し、Path式を使って各アイテムをさすことができます。
例えば、 getPerson スクリプトが name と age をキーにもつ
辞書を返すとすると、それぞれのキーには以下のようにアクセスします :
<span tal:define="person here/getPerson"
tal:replace="string:${person/name} is ${person/age}">
Name is 30</span> years old.
もちろん、スクリプトからZopeのオブジェクトやPythonのリストを返してもかまいま せん。
DTML を呼び出す
スクリプトとは異なり、DTML メソッドや DTML ドキュメントは明示 的なパラメータのリストがありません。 かわりに、クライアント、マッピング、キーワード引数が渡されるこ とになっています。 DTML ではこれらのパラメータを使って名前空間を構成しています。 明示的に DTML を呼び出す方法の詳細は、 「Variables and Advanced DTML(未訳)」 と題された章をご覧ください。
ZopeがDTMLオブジェクトをウェブに公開するとき、オブジェクトのコンテクストを クライアントとして、REQUESTをマッピングとして渡します。 あるDTMLオブジェクトが別のDTMLオブジェクトを読んでいる場合、 名前空間はマッピングにして渡しますが、クライアントは渡されません。
Path式でDTMLオブジェクトをレンダリングすると、 request 、
here 、テンプレートの変数が名前空間に渡されます。
したがって、DTMLオブジェクトはテンプレートとしてコンテキストで
公開されたかのように、同じ名前を使うことができます。
たとえば、JavaScriptを吐くDTMLメソッドを使ったテンプレートはこ
のようになります :
<head tal:define="items here/getItems.sql">
<title tal:content="template/title">Title</title>
<script tal:content="structure here/jsItems"></script>
</head>
...etc...
...また、DTML メソッド jsItems はこのようになっています :
<dtml-let prefix="template.id">
<dtml-in items>
&dtml-prefix;_&dtml-name; = &dtml-value; ;
</dtml-in>
</dtml-let>
DTML は呼び出し直前に定義されたテンプレートの id 変数と items
変数を使っています。
Python モジュール
プログラミング言語 Python にはたくさんのモジュールが付属しています。 モジュールを活用すれば、複雑な処理を手軽に実行することができます。 各モジュールは数学の計算や正規表現のように、ある特定の目的のための Python用の関数、データ、クラスが集まったものです。
"math"モジュールや"string"モジュールを含め、いくつかのモジュールはデフォルト
でPython式で使えます。たとえば、mathモジュールにあるpiの値を得るには、
"python:math.pi" と書きます。Path式では modules 変数を使って"modules/math/pi"のようにします。
"string"モジュールは、"string"式型関数のためにPython式ではそのまま利用できません。
modules 変数経由でアクセスする必要があります。
使う際に直接式を書いたり、次のようにしてグローバル変数を定義することでも
アクセスできます。 :
tal:define="global mstring modules/string"
tal:replace="python:mstring.join(slist, ':')"
ほとんどの場合、stringモジュールの関数ではなく、string メソッドを使うでしょうから、 実際にこのようなことをするのはごく稀なケースでしょう。
モジュールはグループごとにパッケージ化し、関連するモジュールをまとめることができます。 Zope の Python スクリプトは、Zope のパッケージ"Products" のサブパッケージ"PythonScripts"で一まとまりのモジュールになっています。 特に、このパッケージにある"standard"モジュールにはDTMLの"var" タグでよく利用されるフォーマット用の関数がいくつか定義されています。 このモジュールの完全な名前は "Products.PythonScripts.standard" なので、次の文のうちどちらかを使ってアクセスできます :
tal:define="global pps modules/Products.PythonScripts.standard"
tal:define="global pps python:modules['Products.PythonScripts.standard']"
多くのPythonのモジュールには、Page Template、DTML、スクリプト からはアクセスできないようになっています。 "ModuleSecurityInfo" を使って他のPythonモジュールをテンプレートやスクリプト から利用できるようにする方法については Zope Developer's Guide's securitychapter を参照してください。
FTPとWebDAVを使ったリモート編集
Page Templateを編集するには、Webブラウザを使った方法だけでなく、FTPやWebDAVで リモート編集する方法もあります。ここでは、FTPとWebDAVを使ったリモート編集について簡単に解説します。
- サーバとエディタが正しく設定されていることを確認してください。 方法はUsing External Tools をご覧ください。
- エディタから新しい Page Template を作成したい場合、保存する
前に
.ptをファイル名の最後につけてください。こうすることで、 ZopeはPage Template オブジェクトを追加します。 拡張子が.htmlや.zptで終わっているテンプレート、あるいは、index_htmlのように拡張子がないテンプレートを追加したい場合、.ptの拡張子をつけて追加した後、名前を変えるかZMIから追加してく ださい。 - ファイルをエディタで編集し保存します。保存の際は、ファイルを 取得したときと同じURLを使わないといけません。
- 編集後、ページをリロードし、エラーメッセージや何かコメントが ないか確認します。
ファイル名の末尾に .pt がついていないファイルを作成したい場合、
PUT factory をカスタマイズすることでも可能です。詳細は
Using External Tools という章をご覧ください。
テンプレートのキャッシュ
Page Template のレンダリングは、普段は十分に速いものの、あまり速くないときもあります。 頻繁にアクセスされるページやレンダリングに時間のかかるページに対しては、動的な生成をしないでスピードを稼ぎたい場合があるかも知れません。 そのような場合にキャッシュを活用します。 キャッシュについては 「Zope Services(未訳)」という章の 「キャッシュ・マネージャ」の項をご覧ください。
Page Template をキャッシュするには、他のオブジェクトと同じくキャッシュマネー ジャーを使います。Page Template をキャッシュするには、キャッシュマネージャーにテンプレートを 結びつけなくてはいけません。 Page Template の Cache 欄でキャッシュ・マネージャ(Pate Template の獲得できる パスにない場合、Cache は表示されません)を選択するか、キャッシュ・マネージャ の Associate 欄で、Page Template の場所を指定することでキャッシュできます。
Page Template をキャッシュする例です。まず次のようなコンテントの long.py という名前の Python スクリプトを作成します。:
## Script (Python) "long.py"
##
for i in range(500):
for j in range(500):
for k in range(5):
pass
return '終了'
このスクリプトの目的は実行に十分な時間がかかることです。 では、このスクリプトを使った Page Template を作成してみましょう。以下が例です。 :
<html>
<body>
<p tal:content="here/long.py">結果</p>
</body>
</html>
このページを見てみましょう。レンダリングに時間がかかるのがお分かりいただけるで しょう。次に、キャッシュすることで、レンダリング時間を劇的に向上させてみましょう。 まず、Ram Cache Manager がない場合は、作成してください。 Page Template と同じフォルダか、それよりも上のパスに作るようしてください。 Page Template の Cache 欄へ行きます。作成したばかりの Ram Cache Manager を選択し、 Save Changes をクリックします。 Ram Cache Manager の設定を見るには、Cache Settings をクリックします。 デフォルトでは、オブジェクトは1時間(3600 秒)キャッシュされます。 アプリケーションによってこの値は調節した方がいいかもしれません。 元のPage Template へ戻り、もう一度 View します。レンダリングには、しばらく時間 がかかります。そして、ページをリロードすると、すぐにレンダリングされます。 ページを何度リロードしても、キャッシュされているためにすぐにレンダリングされます。
Page Template を変更すると、キャッシュから削除されるため、次回ページを表示する際には、レンダリングに少し時間がかかります。 しかし、いったん表示してしまえば、またレンダリング結果がキャッシュされるで、すぐに表示されます。
キャッシュは単純でありながらパフォーマンスを向上させる強力な技術です。 キャッシュを使うのに高度な知識は不要で、かつスピードを大幅に向上させてくれます。
Zope でのキャッシュの詳細は、「Zope Services(未訳)」という章をご覧ください。
Page Template のユーティリティ
Zope の Page Template は強力でありながらシンプルに作られています。反面、DTMLとは異なり、Page Template にはバッチ、ツリー表示、ソートといったことを手軽に実現する機能はありません。 DTMLにある組み込み機能が使いたい場面は多いでしょう。 こうした要求に答えるために、Zope には Page Template を強化する機能が備わっています。
大量の情報をバッチ処理する
ユーザがデータベースにクエリを投げ、何百もの結果が返ってくる場合、1ページに すべてを表示するよりも、1ページあたり20項目のみにするなどして複数のページに 分けて表示した方がスマートです。 大きなリストを小さなリストに分割することを「バッチ」と呼んでいます。
言語レベルで「バッチ」が組み込まれている DTML とは異なり、Page Template では
ZTUtils モジュールにある特別な Batch オブジェクトを使ってバッチ処理を行
います。 ZTUtils モジュールの詳細については、付録 B の 「API リファレンス(未訳)」をご覧下さい。
Batch オブジェクトは以下のようにして利用します :
<ul tal:define="lots python:range(100);
batch python:modules['ZTUtils'].Batch(lots,
size=10,
start=0)">
<li tal:repeat="num batch"
tal:content="num">0
</li>
</ul>
この例では、10個の要素があるリスト(ここでは、0 から 9 までの数字です)をレンダリングします。
Batch オブジェクトは長いリストを分割してグループや「バッチ」に分割します。
今の場合、100個のアイテムを10個からなる「バッチ」に分割します。
開始した数を変えることによって、別の 10 アイテムからなる「バッチ」を表示することができます。 :
<ul tal:define="lots python:range(100);
batch python:modules['ZTUtils'].Batch(lots,
size=10,
start=13)">
このバッチは 14 個目のアイテムから始まり、23 個目のアイテムで終了します。
つまり、 13 から 22 までの数字を表示します。
バッチの start 引数は最初に表示するアイテムの index であることに注意してください。
インデックスは1からではなく0から始まります。したがって、13 というインデック
スはシーケンスの14個目をさします。Python ではリストにあるアイテムを参照する
のに、インデックスを使います。
バッチ使う場合は、たいていバッチ間で行き来できるようにページにナビゲーション を表示します。 バッチをナビゲーションで行き来するようにしたバッチ処理全体の例です :
<html>
<head>
<title tal:content="template/title">タイトル</title>
</head>
<body tal:define="employees here/getEmployees;
start python:int(path('request/start | nothing') or 0);
batch python:modules['ZTUtils'].Batch(employees,
size=3,
start=start);
previous python:batch.previous;
next python:batch.next">
<p>
<a tal:condition="previous"
tal:attributes="href string:${request/URL0}?start:int=${previous/first}"
href="previous_url">previous</a>
<a tal:condition="next"
tal:attributes="href string:${request/URL0}?start:int=${next/first}"
href="next_url">next</a>
</p>
<ul tal:repeat="employee batch" >
<li>
<span tal:replace="employee/name">Bob Jones</span>
は一年に $<span tal:replace="employee/salary">100,000</span>
稼いでいる。
</li>
</ul>
</body>
</html>
同じフォルダに getEmployees という名前で次のような本文の Script (Python) を 作成します。(パラメータは必要ありません) :
return [ {'name': 'Chris McDonough', 'salary':'5'},
{'name': 'Guido van Rossum', 'salary': '10'},
{'name': 'Casey Duncan', 'salary':'20' },
{'name': 'Andrew Sawyers', 'salary':'30' },
{'name': 'Evan Simpson', 'salary':'35' },
{'name': 'Stephanie Hand', 'salary':'40' }, ]
この例では getEmployees メソッドの結果に対してバッチ処理を繰り返します。一
度のバッチですべてのページにいけるよう、 previous と next のリンクがあり
ます。
ここでのバッチのサイズは 3 です。
body エレメントにある tal:define 文を見てみましょう。
バッチ処理用の変数をいくつか定義しています。 employee 変数は getEmployees
スクリプトが帰る employee オブジェクトのリストです。
現段階では、リストの数はそれほど大きいわけではありませんが、(とくに SQL Method で *本当の*
従業員(employee) を呼び出す場合など)かなり大きくなりかねません。二つ目の変数、
start は request/start の値に設定され、リクエストに start 変数がない場
合は 0 が設定されます。
start 変数は employee のリストでどの位置にいるのかという情報を保持し続けます。
batch 変数は、employee のリストにある10アイテムからなるバッチです。
バッチ処理は start 変数で指定された場所から開始されます。
previous 変数と next 変数はそれぞれ(もしあれば)一つ前のバッチ、次のバッ
チを差します。
これらの変数はすべて body エレメントで定義されているので、body 内にあるす
べてのエレメントで利用できます。
次にナビゲーションリンクを見てみましょう。
これらは一つ前のバッチ、次のバッチを閲覧するためのハイパー・リンクを生成します。
tal:condition 文はまず最初に一つ前のバッチ、次のバッチがあるか確認します。
もしあれば、リンクがレンダリングされ、なければリンクは生成されません。
tal:attributes 文は一つ前のバッチ、次のバッチ用のリンクを生成します。
リンクは単にバッチ処理の開始インデックスを示すクエリ文字列のついたURL、つま
り、現在のページ( request/URL0 )です。
たとえば、現在のバッチのインデックスが 10 から始まっていたとしたら、ひとつ前
のバッチはインデックスが 0 から始まります。 バッチの first 変数が開始イン
デックスを指定するので、この場合、 previous.start は 0 ということになりま
す。
この例の仕組みを完全に理解することは重要ではありません。単にコピーするなり Z Search Interface で作られたバッチの例を使えば十分でしょう。
後になって、もっと複雑なバッチ処理をしたくなったら、サンプルコードをもとに実験してみるとよいでしょう。
ZTUtils モジュールと Batch オブジェクトの詳細は、付録 B の 「API リファレンス」を参照してください。
さまざまなユーティリティ
Zope は Page Template で使うと便利ないくつかの Python モジュールを用意しています。
string 、math 、random モジュールは Python 式で文字列の整形、数学関数、
擬似乱数の生成に使えます。
これらのモジュールは DTML や Script (Python) でも使えます。
Products.PythonScripts.standard モジュールは Script (Python) のユーティリティ
用に設計されたものですが、Page Templates でも利用可能です。
このモジュールには文字列や数字を整形するためのさまざまな関数が含まれています。
この章の最初の方で触れたように、 sequence モジュールには手軽な sort 関数が
用意されています。
最後に、 AccessControl モジュールはアクセス情報を取得したり、認証ユーザを
取得に必要な関数やクラスが含まれています。
これらユーティリティの詳細は 付録 B の API Reference をご覧ください。
結び
この章では、Page Template の便利な活用方法や、細部について触れました。 読み終わってみると、圧倒されてしまうかもしれませんが心配は無用です。 Page Template を使いこなすのにこの章に書かれていることすべてを知っている必要はありません。 「パス」や「マクロ」については知っていなければいけませんが、それ以外のことに ついては、必要になったときにまた読み返せば十分です。 この章で学んだ高度な機能は、必要なった時のためにあるのです。