これまでのおさらい
前回までで、さくらのレンタルサーバーで最安プランであるライトを契約し、CGIしか使えないために、Bottleという1ファイルで実装されているWebフレームワークを用いて、SQLiteと組み合わせて動的Webページを実現しました。
CookieやHTTPヘッダ周り、GETやPOSTでのリクエストへの応答などのほか、静的ページ、エラーページを試してみました。
本稿で取り扱うのはテンプレートのみです。Webのコンテンツという意味では本丸ですね。
テンプレート
いよいよテンプレートです。まずはテンプレート内での詳しい機能についてではなくて、こういう風に使えば動くという全体感をつかむための例を示します。
既存の/helloを改造する形になりますが、新たにgetとviewをimportしてそれぞれ@getと@viewが登場しています。
from bottle import get, view
@get('/hello')
@view('hello.tpl')
def hello():
cur.execute('SELECT * FROM items')
name = None
# 取得したデータはカーソルの中に入る
for row in cur:
name = row[1]
context = {
'name': name,
}
return context
ロジックはとても単純でSQLiteに保管済みのitemsテーブルのアイテムの名前を1つだけ取り出させるような形になっています。実際にアプリを開発するときには、取得した全てのレコードを使って、テンプレート側で取捨選択するようなことも考えられますが、ここでは冒頭に述べたとおり構造の把握を目的にした例にしています。
テンプレート・ファイルはデフォルトではviewフォルダの下に配置することとなっています。ここではhello.tplと指定してあるのでこの名称で作成します。
<html>
<head>
<title>HELLO</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div style="margin-left: 16px; color: red;">{{name}}</div>
</body>
</html>
ロジック側ではcontextを返却していて、テンプレート側ではそのメンバーであるnameに対して{{}}で囲む形でアクセスしているのが分かると思います。
実際にアクセスしてみます。

無事に赤いリンゴが手に入りました。これでPythonプログラム側のデータをテンプレート側で使うイメージが付いたかと思います。
テンプレート(続き)
続いてはforやifを使った例です。
from bottle import get, view
@get('/hello')
@view('hello.tpl')
def hello():
cur.execute('SELECT * FROM items')
context = {
'records': cur,
}
return context
<html>
<head>
<title>HELLO</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
% for record in records:
% if record[2] < 120:
<div style="margin-left: 16px; color: red;">{{ record[1]}} {{ record[2]}}</div>
% else:
<div style="margin-left: 16px; color: blue;">{{ record[1]}} {{ record[2]}}</div>
% end
% end
</body>
</html>
実際にアクセスしてみる前に、main.dbを削除した上で、以下のスクリプトによるデータを投入しています。
# coding=utf-8
import sqlite3
dbname = 'main.db'
# DBに接続する(未作成の場合、作成される)
conn = sqlite3.connect(dbname)
cur = conn.cursor()
# テーブルの作成
cur.execute(
'CREATE TABLE items(id INTEGER, name TEXT, price INTEGER)'
)
# データ登録
cur.execute('INSERT INTO items values(1, "りんご", 100)')
cur.execute('INSERT INTO items values(2, "みかん", 120)')
cur.execute('INSERT INTO items values(3, "ぶどう", 150)')
conn.commit()
conn.close()
無事にpriceが120未満のデータは赤字で、120以上のレコードが青字で表示されています。

forとifを使ってみましたが、要するに%始まりでPythonのコードを書く、HTML内では{{}}で変数にアクセスといった流れになりますが、ここで一つ重要なポイントとして、Pythonコードではおなじみのインデントは気にしなくて良いという点です。その代わりにendが登場しています。
%を以外に、<% %>で囲むと複数行のブロックに対応出来ます。
<%%>で囲んだPythonコードには2つほど通常のPythonコードと異なる点があります。すでに%始まりの話で登場していますが、インデントは無視され、その代わりにendが必要となります。
テンプレート関数
このテンプレート関数という名称はTEMPLATE FUNCTIONSを勝手に訳しただけです。悪しからず。
includeとrebaseを紹介します。
include
まずはincludeからですが、名称通りの動作をします。良くある使いどころは、HTML内の共通部分をパーツ化し、お決まりのヘッダー部分やフッター部分などを文字通りincludeするというものです。
<head>
<title>{{ title }}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<html>
% include('head.tpl', title='Hello')
<body>
% i = 0
% for record in records:
% i += 1
% if record[2] < 120:
<div style="margin-left: 16px; color: red;">{{ i }} {{ record[1]}} {{ record[2]}}</div>
% else:
<div style="margin-left: 16px; color: blue;">{{ i }} {{ record[1]}} {{ record[2]}}</div>
% end
% end
</body>
</html>
この例では、headタグ部分を丸々外だしにして、タイトル部分を外部から指定可能にしている例になります。
rebase
続いてはrebaseです。
hello.tpl
% rebase('base.tpl')
<body>
% i = 0
% for record in records:
% i += 1
% if record[2] < 120:
<div style="margin-left: 16px; color: red;">{{ i }} {{ record[1]}} {{ record[2]}}</div>
% else:
<div style="margin-left: 16px; color: blue;">{{ i }} {{ record[1]}} {{ record[2]}}</div>
% end
% end
</body>
base.tpl
<html>
% include('head.tpl', title='Hello')
{{ ! base }}
</html>
rebase を使うと、そのコンテンツがrebaseで指定したテンプレート内で{{ base }}でアクセス可能になります。
ここで重要なポイントとして、{{ base }}と書いてしまうと、HTMLのタグがエスケープされてしまってレンダリングされません。

こんな感じの残念な内容になります。かならず{{ ! base }}としましょう。!を忘れずに。そしてこれはBottle 0.12 (stable)のドキュメントでは誤って{{ base }}と書かれています。ご注意下さい。
その他
変数が定義済みであればTrueをそうでなければFalseを返すdefineや変数の値をデフォルト値指定で取得するget、変数にデフォルト値を授けるsetdefaultなどがあります。
今回紹介しているのはすべてBaseTemplace APIを実装しているSimple Templateの機能となります。この表現から推測できるかと思いますが、Simple Template以外に変更することももちろん可能です。Bottleには、SimpleTemplate Engineという高速で強力な内蔵テンプレートエンジンが搭載されていますが、今回紹介しているのはすべてSimple Templateの機能となります。
現時点でフルサポートされているテンプレートエンジンはSimple Templateを含めて4つで、以下の通りです。
| Class | URL | Decorator | Render function |
|---|---|---|---|
SimpleTemplate | SimpleTemplate Engine | view() | template() |
MakoTemplate | http://www.makotemplates.org | mako_view() | mako_template() |
CheetahTemplate | http://www.cheetahtemplate.org/ | cheetah_view() | cheetah_template() |
Jinja2Template | http://jinja.pocoo.org/ | jinja2_view() | jinja2_template() |
デフォルトのテンプレートエンジンを切り替えるには以下の様にします。(例としてMakoTemplate)
from bottle import mako_view as view, mako_template as template
以上、テンプレートについてまとめてみました。