helpers はテンプレートで使用することを想定した関数で、一般的な HTML と テキスト処理、 HTML タグビルダー (それは変数を安全にエスケープする) の ようなより高レベルの構築、およびデータセットのページングのような高度な 機能性を補助します。
Pylons で利用可能な helpers の大部分は webhelpers パッケージによっ て提供されます。また、これらの helpers のいくつかは、テンプレートの中で 他の helpers によって使われるデータを準備するため、コントローラでも使用 されます。例えば authenticate_form() に 対応する secure_form_tag() 関数などです。
個々の helpers をテンプレートの中で h の下で使えるように、適切 な関数が lib/helpers.py でインポートされる必要があります。そう すると、このファイルで利用可能なすべての関数が、他のモジュール参照と全 く同じように h の下で利用可能です。
lib/helpers.py モジュールをカスタマイズすることで、テンプレート で使うためのカスタム関数とクラスをすぐに加えることができます。
ヘルパー関数はテーマ別にモジュールに組織化されます。 webhelpers 直 下のいくつかのサードパーティー製のモジュールを除き、すべての HTML ジェ ネレータは webhelpers_html パッケージの下にあります。 webhelpers モ ジュールは別にドキュメント化されます。 webhelpers を参照してくだ さい。
Note
paginate モジュールは、 Webhelpers パッケージの以前のバージョンで 提供されていた非推奨 (deprecated) の pagination モジュールとは互 換性がありません。
SQL クエリの結果のような多量のデータを表示する場合、通常すべての結果を 1 ページに表示できるわけではありません。 それは単純にあまりに多いでしょ う。 そこで、データをより小さな塊に分割します。 これは paginator が行う ことです。 それは一度に 1 ページ分のデータの塊を表示します。例えば、 Web を通して会社の電話帳を提供していて、ユーザにエントリを検索させるこ とを想像してください。検索結果が 23 のエントリを含むと仮定します。 あな たは 1 ページあたり 10 未満のエントリを表示すると決めることができます。 最初のページはエントリ 1-10 、 2 番目は 11-20 、そして 3 番目は 21-23 を含んでいます。 そして、ユーザが利用可能なページを切り換えることができ る Page 1 of 3: [1] 2 3 のようなナビゲーション要素を表示します。
webhelpers パッケージはこのために使用できる paginate モジュー ルを提供します。 それは簡単な Python リストに加えて SQLAlchemy のクエリ と select オブジェクトからページを作成することができます。そのモジュー ルは、より大きな結果セットからの 1 ページ分のアイテムを表現する Page オブジェクトを提供します。 そのような Page は主にそのペー ジのアイテムのリストのように振る舞います。上記の、 23 アイテムが 3 ペー ジに分割された例を挙げましょう:
# Create a list of items from 1 to 23
>>> items = range(1,24)
# Import the paginate module
>>> import webhelpers.paginate
# Create a Page object from the 'items' for the second page
>>> page2 = webhelpers.paginate.Page(items, page=2, items_per_page=10)
# The Page object can be printed (__repr__) to show details on the page
>>> page2
Page:
Collection type: <type 'list'>
(Current) page: 2
First item: 11
Last item: 20
First page: 1
Last page: 3
Previous page: 1
Next page: 3
Items per page: 10
Number of items: 23
Number of pages: 3
# Show the items on this page
>>> list(page2)
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# Print the items in a for loop
>>> for i in page2: print "This is entry", i
This is entry 11
This is entry 12
This is entry 13
This is entry 14
This is entry 15
This is entry 16
This is entry 17
This is entry 18
This is entry 19
This is entry 20
Page オブジェクトを呼び出すためのさらなるパラメタがあります。 webhelpers.paginate.Page を見てください。
Note
ページ番号とアイテム番号は 1 から始まります。インデックスによってペー ジアイテムにアクセスしているなら、最初のアイテムは item[0] では なく item[1] であることに注意してください。
ユーザは他のページに移動する方法を必要とします。 これは通常、 Page 3 of 41 - 1 2 [3] 4 5 .. 41 のようなリンクのリストによって実現されます。 そのようなリストは Page の pager() メソッ ドで作成できます。 もう一度上記の例を見てください:
>>> page2.pager()
<a class="pager_link" href="/content?page=1">1</a>
<span class="pager_curpage">2</span>
<a class="pager_link" href="/content?page=3">3</a>
HTML タグがなければ、それは 1 [2] 3 のように見えます。それらのリン クは各ページが見つかる URL を指しています。そして、現在のページ (2) は 強調されています。
pager の見た目はカスタマイズできます。デフォルトではフォーマット文字列 は ~2~ で、現在のページから最大半径 2 の隣接するページを表示するこ とを意味します。より大きなセットでは、これは 1 .. 34 35 [36] 37 38 .. 176 のように表示されるでしょう。半径 2 は、現在のページ 36 の前後 に 2 ページが表示されることを意味します。
フォーマット文字列でいくつかの特別な変数を使用できます。 全リストについ ては pager() を見てください。 20 ページ のうち現在 10ページ目を表示している pager のいくつかの例:
>>> page.pager()
1 .. 8 9 [10] 11 12 .. 20
>>> page.pager('~4~')
1 .. 6 7 8 9 [10] 11 12 13 14 .. 20
>>> page.pager('Page $page of $page_count - ~3~')
Page 10 of 20 - 1 .. 7 8 9 [10] 11 12 13 .. 20
>>> page.pager('$link_previous $link_next ~2~')
< > 1 .. 8 9 [10] 11 12 .. 20
>>> page.pager('Items $first_item - $last_item / ~2~')
Items 91 - 100 / 1 .. 8 9 [10] 11 12 .. 20
ページのデータが SQLAlchemy を通してデータベースからやって来たものなら、 paginate モジュールは直接 query オブジェクトにアクセスできます。 これは ORM にマッピングされたモデルを使用するときに便利です。 例:
>>> employee_query = Session.query(Employee)
>>> page2 = webhelpers.paginate.Page(
employee_query,
page=2,
items_per_page=10)
>>> for employee in page2: print employee.first_name
John
Jack
Joseph
Kay
Lars
Lynn
Pamela
Sandra
Thomas
Tim
paginate モジュールは十分賢いので、このページで必要なオブジェクトだけ をデータベースに問い合わせることができます。例えば、 1 ページがアイテム 11-20 から成る場合、 SQLAlchemy は実際の SQL クエリにおいて LIMIT と OFFSET を通してまさにその 10 列を取得するように依頼されるでしょう。 そのため、完全な結果セットをメモリに読み込んでからそれを渡してはいけま せん。 Page を作成するときには代わりにいつも query を渡してください。
また、 SQLAlchemy はデータベースのテーブルに対する任意の SELECT 文を実 行することが可能です。これは非 ORM クエリに便利です。 paginate はその ような select オブジェクトも使用できます。 例:
>>> selection = sqlalchemy.select([Employee.c.first_name])
>>> page2 = webhelpers.paginate.Page(
selection,
page=2,
items_per_page=10,
sqlalchemy_session=model.Session)
>>> for first_name in page2: print first_name
John
Jack
Joseph
Kay
Lars
Lynn
Pamela
Sandra
Thomas
Tim
SQLAlchemy query オブジェクトを使用することとの唯一の違いは、 sqlalchemy_session パラメタで SQLAlchemy session を渡す必要がある ということです。 素の select は割り当てられたデータベース接続を持っ ていません。 しかし、セッションは持っています。
始めるための簡単な例。
Controller:
def list(self):
c.employees = webhelpers.paginate.Page(
model.Session.query(model.Employee),
page = int(request.params['page']),
items_per_page = 5)
return render('/employees/list.mako')
Template:
${c.employees.pager('Page $page: $link_previous $link_next ~4~')}
<ul>
% for employee in c.employees:
<li>${employee.first_name} ${employee.last_name}</li>
% endfor
</ul>
pager() は以前の URL へのリンクを作成して、単に適切に page パラメタ を設定します。そんなわけで、 Page を作成するときにリクエストされたペー ジ番号 (request.params['page']) を渡す必要があります。
ページを部分的にアップデートすることは簡単です。それに必要なのは、 (完 全なページをロードする代わりに) ページングアイテムを含むページの一部を アップデートする小さな Javascript だけです。 pager() メソッドはその 目的のために onclick パラメタを受け付けます。その値は onclick パラメタとして A-HREF タグに追加されます。それで、 href パラメタは 完全なページをロードする URL を指す一方、 onclick パラメタが部分的 なページをロードする Javascript を提供します。例 (簡単のために jQuery Javascript ライブラリを使用します) は、その説明の助けになるでしょう。
Controller:
def list(self):
c.employees = webhelpers.paginate.Page(
model.Session.query(model.Employee),
page = int(request.params['page']),
items_per_page = 5)
if 'partial' in request.params:
# Render the partial page
return render('/employees/list-partial.mako')
else:
# Render the full page
return render('/employees/list-full.mako')
Template list-full.mako:
<html>
<head>
${webhelpers.html.tags.javascript_link('/public/jQuery.js')}
</head>
<body>
<div id="page-area">
<%include file="list-partial.mako"/>
</div>
</body>
</html>
Template list-partial.mako:
${c.employees.pager(
'Page $page: $link_previous $link_next ~4~',
onclick="$('#my-page-area').load('%s'); return false;")}
<ul>
% for employee in c.employees:
<li>${employee.first_name} ${employee.last_name}</li>
% endfor
</ul>
テンプレートのコードが重複するのを避けるため、完全なテンプレートは部分 的なテンプレートを include しています。 部分的なページロードがリクエス トされるなら、 list-partial.mako だけがレンダリングされます。 また、 全ページロードがリクエストされるなら、 list-partial.mako がレンダリ ングされ、それが今度は list-full.mako を include します。
onclick 文字列中の %s 変数は、それぞれのページを示す URL に partial=1 が加えられたものと置き換えられます (パラメタの名前は partial_param パラメタを通してカスタマイズできます)。 例:
ある DOM オブジェクト (例えば、 DIV) に URL をロードする jQuery の構文 は単に以下の通りです:
$('#some-id').load('/the/url')
このテクニックの利点は優雅に退行 (degrade) するということです。もしユー ザが Javascript を有効にしていなければ全ページがロードされます。そして、 Javascript が動作しているなら onclick 動作で部分ロードが行われます。
クロスサイトリクエストフォージェリ (CSRF) 攻撃防止のために。
destined のウェブアプリによって検証されるクライアント固有の権限トークン を含むフォームタグを生成します。
権限トークンはクライアントのセッションの中に保存されます。 そして、ウェ ブアプリはリクエストの送信された権限トークンをクライアントのセッション の中にある値と共に検証することができます。
これは、リクエストが元のページから来たことを保証します。詳しい情報に関 しては wikipedia の クロスサイトリクエストフォージェリ の項を見てく ださい。
Pylons はコントローラに代わってこの検証を行う authenticate_form デ コレータを提供します。
これらの helpers は Pylons の session オブジェクトに依存しています。 それらの大部分は、API 呼び出しを変えることによって別のフレームワークに 容易に移植できます。
それらの helpers は、 AJAX 呼び出しに helpers を使用するなら開発者が簡 単に自身の helpers を作成できるような方法で実装されています。 (訳注: この段落が前後の段落とどうつながるのかちょっと分からない)
authentication_token() は現在の認証トークンを返します。まだ存在し ていないなら、認証トークンを 1 つ作成して、それをセッションの中に保存し ます。
auth_token_hidden_field() は認証トークンを含む hidden フィールド を作成します。
secure_form() は form() + auth_token_hidden_field() です。