はじめに
本連載では、Python上で動作するWebサーバ&WebアプリケーションフレームワークであるTornadoでアプリケーション開発を行う方法について紹介します。本連載で扱う内容は、次のとおりです。
- Tornadoの特徴
- Tornado利用のための環境設定手順
- Hello, Worldアプリケーションの作成
- データベースと連携したアプリケーションの作成
- その他、役立つTornado関連のテクニック
第2回目の今回は、テンプレートを使った動的なページの生成について紹介します。
対象読者
- PythonによるWebアプリケーション開発に興味がある方
- Pythonを触ったことがある方
- Webアプリケーションの開発をこれから学ぶ方、もしくは学び始めたばかりの方
必要な環境
- Python 2.6 以上
- Tornado 4.2
関連記事
サンプルコード
前回のおさらい
前回の記事では以下のようなHello Worldを表示するコードを作成しました。
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.current().start()
上記のコードは、http://localhost:8888/にGETリクエストをすると、MainHandlerのgetメソッドが「Hello, World」という文字列を返してブラウザに表示するというものでした。しかし、上記のコードのように固定された値だけを返して表示するのでは、ユーザの入力に応じて表示を変えるような柔軟なアプリケーションが作れません。そのようなことをするために、Tornadoを含むほとんどのWebアプリケーションフレームワークにはプログラムから動的にページを生成する機能があります。そこで、今回はTornadoで動的にページを生成する機能について紹介していきます。ここでのキーワードは以下の2つです。
- テンプレート
- テンプレートエンジン
以降ではまず、テンプレートとテンプレートエンジンについて解説し、その後、実際に動的にページを生成します。
テンプレートとテンプレートエンジン
テンプレート(template)とはひな形のことです。Webアプリケーション開発におけるテンプレートは、変化する部分と変化しない部分からなります。このうち、変化する部分には置き換えるための文字列を埋め込んでおき、必要に応じてプログラムからデータを埋め込みます。この際、データを置き換えるために使うのがテンプレートエンジンです。つまりテンプレートエンジンは、テンプレートに埋め込まれた変化する文字列を置き換えて新たな文字列を作る仕組みのことです。
簡単な例をあげて見てみましょう。以下はテンプレートの例です。
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% end %}
</ul>
</body>
</html>
上記をみてわかるように、テンプレートはほとんどHTMLです。**Tornadoのテンプレートはマークアップに埋め込まれた制御構造と式からなります。**このテンプレートには変化する部分と変化しない部分があります。変化するのは波かっこで囲まれた{{ title }}と{% for %}{% end %}で囲まれた部分、変化しないのはHTMLのタグの部分です。
テンプレートエンジンに上記のテンプレートと置き換えるためのデータを渡すことで、新たな文字列を作ることができます。例えば、{{ title }}としてFruits List、itemsとして**['apple', 'orange', 'grape']**を渡すとそれぞれの部分が置き換えられ、以下のような文字列が生成されます。
<html>
<head>
<title>Fruits List</title>
</head>
<body>
<ul>
<li>apple</li>
<li>orange</li>
<li>grape</li>
</ul>
</body>
</html>
テンプレートの機能をうまく使うことで、変化する部分と変化しない部分を切り分けて効率的に開発できるようになります。
作成するアプリケーション
今回作成するのは、名前を受け取ったら「Hello yourname!」のように表示するアプリケーションを作成します。簡単なアプリケーションですが、このアプリケーションの作成を通じて、動的なページ生成の基礎について理解します。アプリケーションのイメージとしては以下のようになっています。
ここでは、名前としてHironsanという文字列を渡しています。
ディレクトリ構成
コードを見る前に、まずは今回のディレクトリ構成について見ていきます。今回は以下のようなディレクトリ構成となっています。
.
├── app.py
├── static
│ └── style.css
└── templates
└── index.html
前回と大きく異なる点は、staticディレクトリとtemplatesディレクトリがあるところです。今回はstaticにstyle.cssファイル、templatesにindex.htmlファイルを格納しています。これらのディレクトリの一般的な使われ方としては、templatesディレクトリにはHTMLファイルを入れ、staticディレクトリにはCSSファイル、JavaScriptのコード、画像などをそれぞれディレクトリに分けて格納します。
以上より、今回のアプリケーションの構成要素は以下の3つに分けられます。
- Pythonコード
- テンプレート
- CSSファイル
ではまずはPythonのコードから見てみましょう。
Pythonコード
今回実行するPythonコードは以下のようなものです。
import os
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
name = self.get_argument('name', 'World')
self.render('index.html', name=name)
BASE_DIR = os.path.dirname(__file__)
application = tornado.web.Application([
(r'/', MainHandler),
],
template_path=os.path.join(BASE_DIR, 'templates'),
static_path=os.path.join(BASE_DIR, 'static'),
)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.current().start()
前回のコードとの大きな違いは以下の3点です。
- Applicationオブジェクトの作成時にtemplate_pathとstatic_pathを渡している
- getメソッドの中で、renderメソッドを使用している
- getメソッドの中で、get_argumentを用いて名前を受け取っている
template_pathとstatic_path
前回説明したように、Applicationオブジェクトはアプリケーション全体の設定をするために使えるのでした。このApplicationオブジェクトの引数にtemplate_pathとstatic_pathを渡すことで、**アプリケーションにtemplatesディレクトリとstaticディレクトリがどこにあるかを教えています。**これらを渡さないとアプリケーションはHTMLファイルがどこにあるのかわからないので、テンプレート等の読み込みに失敗し以下のようなエラーが出ます。
FileNotFoundError: [Errno 2] No such file or directory: '/hogehoge/index.html'
render
前回用いたwriteメソッドは文字列を引数にとり、その文字列をHTTPレスポンスとして返すメソッドです。それに対して、renderメソッドはテンプレートファイルと指定した引数をテンプレートエンジンに渡し、その結果生成された文字列をレスポンスとして送信するメソッドです。今回は以下のように引数を渡しています。
self.render('index.html', name=name)
第一引数はテンプレートのファイル名であり、第二以降の引数はテンプレートに渡す変数です。ここでは、テンプレートのファイル名は'index.html'であり、変数としてnameという名前でnameを渡しています。nameという名前で引数を渡しているので、テンプレート中ではnameという名前で参照できます。これがもし、yourname=nameだったら、テンプレート中ではyournameという名前で参照します。
結局のところ、Applicationオブジェクトの中でtemplate_pathを設定したのは、Tornadoにtemplatesディレクトリの場所を教えて、renderメソッドからテンプレートファイルを読み込むためだったというわけです。
get_argument
get_argumentメソッドは、クライアントから渡されたパラメータを受け取るためのメソッドです。TornadoのRequestHandlerクラスには役に立つ組み込みのメソッドがいくつもあります。そのうちの一つがget_argumentです。
例えば、今回の場合以下のように指定しています。
name = self.get_argument('name', 'World')
get_argumentの第一引数に指定した名前の値がパラメータから得られます。パラメータにget_argumentメソッドの第一引数に指定した名前がなかった場合には、get_argumentメソッドの第二引数の値が返されます。つまり、第二引数の値はデフォルト値を設定しているということです。もし、get_argumentに第二引数を設定せず、第一引数に指定した名前がパラメータになかった場合にはエラーが発生します。
次に、htmlファイルを見てみましょう。
テンプレート
今回表示に使うテンプレートは以下のようになっています。
<html>
<head>
<title>Hello Tornado</title>
<link rel="stylesheet" href="{{ static_url('style.css') }}" />
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
テンプレートの中に波括弧で囲われている部分があるのがわかるでしょうか?これらは必要に応じてデータを渡すことで置き換えることができます。{{ name }}にはrenderメソッドから渡された値が入り、{{ static_url('style.css') }}にはstyle.cssのパスが入ります。
ここで使われているstatic_url()メソッドは、メソッド内に指定したファイルへの相対パスがURLに変換されます。また、URLに変換する際にはApplicationオブジェクトで設定したstatic_pathの値が使われます。今回はstatic_pathとしてstaticディレクトリを設定したのを思い出してください。
static_url()メソッドによる変換の例として、static_url('style.css')の場合は、/static/style.cssのように変換され、static_url('css/style.css')の場合は、/static/css/style.cssのように変換されます。
最後にCSSを見てみましょう。
CSSファイル
今回HTMLのスタイルを指定するために使うCSSファイルは以下のようなものです。
body {
background-color: #FFFFE0;
}
h1 {
background-color: #99cc00;
}
このCSSファイルについては、Tornado特有の機能を使っている訳でもないので、説明は省かせていただきます。
実行
前回と同じように、以下のコードを実行してサーバを立ち上げてください。
$ python app.py
その後、http://localhost:8888/?name=yournameにアクセスしてください。「Hello, yourname!」と表示されたはずです。URLのyournameの部分をいろいろな文字列に変えてみてください。あなたの渡した文字列に応じて動的にページが生成されていることがわかるはずです。
まとめ
今回は、テンプレートとテンプレートエンジンを用いた動的なページ生成について紹介しました。プログラムからテンプレートにデータを渡すことで、動的にページを生成できることがご理解いただけたと思います。今回はstaticの直下にcssファイルを置きましたが、実際にはcssディレクトリやstylesheetディレクトリを作成し、その下にcssファイルを置くことが多いです。このあたりのプロジェクト構成の話はいずれします。
次回はTornadoのテンプレートについて詳しく紹介します。