[GAE] Google App Engine で TODO アプリケーションを作ってみる

Getting Started Guide はなぞってみたので、今度は TODO アプリケーションを作ってみる。
Google App Engine で Tropy っぽいやつ作ってみた - IT戦記 を参考にしました。

うわっ Python のメソッドって self を引数に def するのか。モデルを定義 Todo クラス。done には BooleanProperty を使おう。

MainPage はログインしていなければログイン画面にリダイレクト。ログインしていれば、ログインユーザの Todo を取得して indext.html テンプレートを表示する。GQL を書くかわりに Mode の all, filter, order を使ってみる。

AddPage では get できたときは、追加ページテンプレート(add.html)を表示。post できたときは、リクエストパラメータから Todo を組み立ててデータストアに保存。日付は 20080426 形式での入力を想定。文字列から日付への変換はこれでいいのか。

DoneHandler は済のチェックボックスクリックで、id をもとに Todo を取得(get_by_id)して、done をトグルしてデータストアを更新。更新も登録と同じく put() で行なう。

DeleteHandler では DoneHandler と同じ。put() ではなく delete() を使う。

ま、こんなところか。

app.yaml

application: todo
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: todo.py

todo.py

# python2.5 ~/local/opt/google_appengine/dev_appserver.py ~/letter/gae/todo/
import cgi
import os
import time
import datetime
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import template

#### モデル
class Todo(db.Model):
    owner = db.UserProperty()
    content = db.StringProperty()
    deadline = db.DateProperty()
    done = db.BooleanProperty()

#### メインページ
class MainPage(webapp.RequestHandler):
    ### ログインしていない場合はログイン画面へ
    def get(self):

        if users.get_current_user():
            self.list_todo()
        else:
            self.redirect(users.create_login_url(self.request.uri))

    ### ログインユーザの TODO を取得してテンプレートへ
    def list_todo(self):

        todos = Todo.all().filter(
            "owner =", users.get_current_user()).order("deadline")

        path = os.path.join(os.path.dirname(__file__), 'index.html')
        logout_url = users.create_logout_url(self.request.uri)
        self.response.out.write(
            template.render(path, { 'todos': todos,
                                    'logout_url': logout_url }))

#### TODO 追加ページ
class AddPage(webapp.RequestHandler):
    ### TODO 追加ページのテンプレートへ
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'add.html')
        self.response.out.write(template.render(path, {}))

    ### TODO をデータストアに保存して / にリダイレクト
    def post(self):
        todo = Todo()
        todo.owner = users.get_current_user()
        todo.content = self.request.get('content')
        todo.deadline = datetime.date(
            *time.strptime(
                 self.request.get('deadline'), "%Y%m%d")[0:3])
        todo.done = False
        todo.put()              # データストアに保存
        self.redirect('/')

#### 済チェックボックスクリック
class DoneHandler(webapp.RequestHandler):
    def get(self):
        todo = db.get(db.Key(self.request.get('key')))
        todo.done = not todo.done
        todo.put()
        self.redirect('/')

#### 削除ボタン
class DeleteHandler(webapp.RequestHandler):
    def get(self):
        todo = db.get(db.Key(self.request.get('key')))
        todo.delete()           # データストアから削除する。
        self.redirect('/')

def main():
    application = webapp.WSGIApplication(
        [('/', MainPage),
         ('/add', AddPage),
         ('/done', DoneHandler),
         ('/delete', DeleteHandler)],
        debug=True)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
    main()

index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>TODO アプリケーション : Google App Engine</title>
  </head>

  <body>
    <h1>TODO</h1>

    <a href="/add">TODO 追加</a>

     <table>
      <tr>
        <th>内容</th><th>締切</th><th></th><th>&nbsp;</th>
      </tr>
      {% for each in todos %}
      <tr>
        <td>{{each.content}}</td>
        <td>{{each.deadline}}</td>
        <td><input type="checkbox"
              onclick="location.href='/done?key={{each.key}}'"
              {% if each.done %} checked{% endif %}></td>
        <td><input type="button" value="削除"
              onclick="location.href='/delete?key={{each.key}}'"></td>
      </tr>
      {% endfor %}
    </table>

    <a href="{{logout_url}}">ログアウト</a>

  </body>
</html>

Model は key をパラメータにして、次のように取得する。http://code.google.com/appengine/docs/datastore/creatinggettinganddeletingdata.html

db.get(db.Key(self.request.get('key')))

add.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>TODO 追加ページ</title>
  </head>

  <body>
    <h1>TODO を追加します</h1>

    <form action="/add" method="post">
      内容 <input type="text" name="content">
      締切 <input type="text" name="deadline">
      <input type="submit" value="追加する">
    </form>

    <a href="/">キャンセル</a>
  </body>
</html>