最近iMacを買ったのだが、その静かさに驚いている。しかし、そうなると今度は隣で動いている自宅サーバーのうるささが気になってくる。というわけで自宅サーバを止めた。しかし、さくらのレンタルサーバーを再び管理するのは面倒だ・・。ということで、ここは一丁GAEにチャレンジしてみるかということで、以下のページを参考に触ってみる。
http://code.google.com/appengine/docs/gettingstarted/
こういうものが出来上がる
特徴
GAEで現在サポートしているのはpythonのみ。Djangoをベースにした、webappフレームワークが利用可能。Ruby on RailsやJavaは今のところ使えません。
アプリのファイル構成は以下のような感じ。
- app.yaml: 言語、APIの指定、ルーティングの指定など
- helloworld.py: MVCのM,C
- index.html: MVCのV
開発手順は以下の通り。
ハンドラ、モデルのソースコード
ハンドラ、モデルのソースコードは以下のような感じ。
[helloworld.py]
import cgi import wsgiref.handlers import os 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 Greeting(db.Model): author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True) class MainPage(webapp.RequestHandler): def get(self): query = Greeting.all().order("-date") greetings = query.fetch(10) if users.get_current_user(): url = users.create_logout_url(self.request.uri) url_linktext = 'Logout' else: url = users.create_login_url(self.request.uri) url_linktext = 'Login' template_values = { 'greetings': greetings, 'url': url, 'url_linktext': url_linktext, } path = os.path.join(os.path.dirname(__file__), 'index.html') self.response.out.write(template.render(path, template_values)) class Guestbook(webapp.RequestHandler): def post(self): greeting = Greeting() author = users.get_current_user() if author: greeting.author = author greeting.content = self.request.get('content') greeting.put() self.redirect('/') def main(): application = webapp.WSGIApplication( [('/', MainPage), ('/sign', Guestbook)], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()
ここでは、Greetingというモデルと、Main, Guestbookというリクエストハンドラを定義している。main()でリクエストハンドラとパスのマッピングをやっている。あとはお約束かな。
コントローラ(リクエストハンドラ)は以下のような感じで定義。
- リクエストハンドラクラスは、webapp.RequestHandlerを継承する。
- 1アクション:1リクエストハンドラを定義するのが基本。
- get,postというメソッドがそのままhttpのメソッドに対応
- レスポンスの描画は、self.response.out.write("...")
- templateを使って、self.response.out.write(teplate.render('index.html', template_values))でもOK. 普通はこっちを使う. template.renderメソッドの第一引数はテンプレートファイル名、第二引数はhtmlに埋め込む動的な文字列のためのマップです。
- Googleアカウントを操作するためのgoogle.appengine.api.usersなどのAPIが利用可能。
モデルは以下のような感じで定義。
- モデルクラスは、db.Modelを継承する。
- モデルクラスを通じて、DatastoreというDBっぽいものを操作できる
- ここで定義したクラス名が、そのままテーブル名になる
- さらにクラス内でcontent = db.StringProperty(..)とか定義すると、これがそのままテーブルの列名になる(content,という列ができる)。
- モデルのインスタンスを作って、プロパティ値をセットし、putメソッドを呼べばDatastoreにデータを格納できる。
- データを取得するには、SQLに似たGQLを利用する。
- Datastoreは普通のRDBとは感じがちがうっぽい。
- Greeting.all().order('-date')という感じで、RailsのARぽくデータを取得することも可能。
ビュー
で、以下がビューのテンプレート。
[index.html]
<html> <head> <link type="text/css" rel="stylesheet" href="/stylesheets/main.css" /> </head> <body> {% for greeting in greetings %} {% if greeting.author %} <b>{{ greeting.author.nickname }}</b> wrote: {% else %} An anonymous person wrote: {% endif %} <blockquote>{{ greeting.content|escape }}</blockquote> {% endfor %} <form action="/sign" method="post"> <div><textarea name="content" row="3" cols="60"></textarea></div> <div><input type="submit" value="Sing Guestbook"></div> </form> <a href="{{ url }}">{{ url_linktext }}</a> </body> </html>
{{% .. %}}とか{{ .. }}に、リクエストハンドラで渡された文字列を埋め込む感じ。
ハマり事項
今回の程度だと、ほとんどハマることはない。ただ、リクエストハンドラにおいて以下のコードを
query = Greeting.all().order("-date")
greetings = query.fetch(10)
以下のように書くと、
greetings = Greeting.all().order("-date").fetch(10)
時々エラーが発生する。なぜだろ。