テンプレートを使ってみました

以前書いていたコードではPythonによるプログラム本体に、出力するHTMLがすべて文字列として埋め込まれていたため、HTML編集にIDEによるアシストや構文チェックが使えないのを不便に感じていました。
そこでテンプレートの機能を使ってPythonのコードとHTMLを別のファイルに記述するようにしました。

テンプレートは厳密にはGAEの機能ではなくDjangoやJinja2といったフレームワークの機能ですがこれらはGAEのSDKに付属しており本番環境でも個別にインストールすることなく使用できます。

今回はチュートリアルのコードに倣っているためJinja2を使用しています。

Jinja2をimportする際ライブラリにPATHが通っていないと、Eclipseが"Unresolved import"というエラーを出すのですがそのままでも問題なく動作します。
ただエラーが出たままだとうっとうしいので

import jinja2  # @UnresolvedImport

とコメントで書いておくとエラーを消すことができます。

8/29追記。
Eclipseの設定で「プロジェクト」→「プロパティ」→「PyDev-PYTHONPATH」のExternal Librariesに"{GOOGLE_APP_ENGINE}/lib/jinja2"といった具合でPATHを追加するとコメントを書かなくてもエラーが出なくなるそうです。

テンプレートの本来の(?)使い方はコードとHTML部分の分離というより、別に用意されたHTMLファイルの一部に動的に変更を加えて出力することだと思うのですが今回のコードでは変更を加える箇所が1箇所のみだったため該当するHTMLファイルのみ下に掲載しています。
機能は以前書いていたコードと全く同じで、名前、性別、身長、体重を登録してそれを検索できるというものです。

# coding: utf-8

import webapp2
import cgi
import os
import jinja2  # @UnresolvedImport
from google.appengine.ext import db

jinja_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))


class DataStoreTest(db.Model):
    name = db.StringProperty()
    sex = db.IntegerProperty()
    height = db.FloatProperty()
    weight = db.FloatProperty()
    date = db.DateTimeProperty(auto_now_add=True)


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/html;charset=utf-8'

        template_values = {}
        template = jinja_environment.get_template('index.html')
        self.response.out.write(template.render(template_values))

    @staticmethod
    def _dataformer(person):
    "動的に変更を加える部分のHTMLを作成する"
        sexL = [u'男', u'女', u'未登録']

        text = '<tr><td>'
        text += cgi.escape(person.name)
        text += '</td><td>'
        text += sexL[person.sex - 1]
        text += '</td><td>'
        text += cgi.escape(str(person.height))
        text += '</td><td>'
        text += cgi.escape(str(person.weight))
        text += '</td></tr>'

        return text

    def post(self):
        req_work = int(self.request.get('isReg').strip())
        req_name = self.request.get('Name').strip()

        if not req_name:  # Name未記入の場合はgetの場合を実行
            self.get()
            return

        if req_work:
            try:
                person = DataStoreTest(name=req_name)
                person.sex = int(self.request.get('Sex'))
                person.height = float(self.request.get('Height'))
                person.weight = float(self.request.get('Weight'))
            except ValueError:
                self.get()
                return

            person.put()

            self.response.headers['Content-Type'] = 'text/html;charset=utf-8'

            template_values = {}
            template = jinja_environment.get_template('comp.html')
            self.response.out.write(template.render(template_values))
            return

        if not req_work:
            persons = (DataStoreTest
                       .all()
                       .filter('name =', req_name)
                       .order('-date'))

            persontext = ''
            for person in persons:
                persontext += self._dataformer(person)

            self.response.headers['Content-Type'] = 'text/html;charset=utf-8'

            template_values = {'persontext': persontext}
            template = jinja_environment.get_template('result.html')
            self.response.out.write(template.render(template_values))

app = webapp2.WSGIApplication([('/', MainPage)],
                            debug=True)

検索結果を表示するHTMLファイル。
{{ }}で囲まれた部分が動的に置き換えられる。

<!DOCTYPE html>
<html>
  <head>
    <title>
      問い合わせ結果
    </title>
    <style type="text/css">
      <!--
        table,tr,td {border: solid 1px black; border-collapse: collapse;}
      -->
    </style>
  </head>
  <body>
    <table>
      <thead>
        <tr><td>名前</td><td>性別</td><td>身長</td><td>体重</td></tr>
      </thead>
      <tbody>
      {{ persontext }}
      </tbody>
    </table>
    <br>
    <a href="/">戻る</a>
  </body>
</html>