logmeは、自分が聴いた音楽・読んだ本・見た映画を淡々と記録するWebサービスです。ほかのユーザーと情報を共有しコメントによるコミュニケーションを行うことで、新しい音楽・本・映像体験を生み出すことを目指します。
僕は以前「音ログ」というサービスを開発していました。iTunesで聴いた音楽のタイトルをサーバにアップロードして履歴を残せるというものでしたが、iTunesの情報を取得するために専用のクライアントソフトウェアが必要で、もっと手軽に履歴を残したいという要望を多くのユーザーからいただいていました。
logmeは、ブラウザのUIから手動で操作する音ログともいえるサービスで、対象となるメディアを拡張し、クライアントソフトウェアを不要にすることで、幅広いユーザーに使ってもらえることを目指しています。
上記の大まかな構成案を基に、GAEを使ってサービスを開発していきます。
まずは、アプリケーションプロジェクトの置き場所として、適当な名前でディレクトリを作成します(ここではlogme)。以降、すべてのディレクトリ/ファイルはこのディレクトリの下に配置される前提でお読みください。
GAEにアプリケーションの基本情報を伝えるための構成ファイル、app.yamlを作成します。内容は下記のようになります。
application: logme version: 1 runtime: python api_version: 1 handlers: - url: / script: index.py - url: /.* script: not_found.py
applicationに自分のアプリケーションIDをセットします。handlersでURLとハンドラが記述されたファイルのマッピングを行います。
app.yamlの詳細な説明はGAEデベロッパーサイトの“アプリの構成”を参照してください。 app.yamlをはじめとするGAEの設定ファイルは、YAMLで記述します。YAMLについては“The Official YAML Web Site”をご覧ください。
上記app.yamlからサイトトップのページを処理するindex.pyはこんな構成になります。
from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app class Index(webapp.RequestHandler): def get(self): self.response.headers['Content-Type'] = 'text/plain' self.response.out.write('Welcome to logme!') application = webapp.WSGIApplication( [('/', Index)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()
モジュールwebappをインポートして、webapp.RequestHandlerを継承するハンドラクラスIndexを定義しています。
WSGIApplicationクラスを実体化する際に、このハンドラとURLのペアを初期化パラメータとして渡しています。これにより、index.pyが実行された際にURLと対応したハンドラが実行されます。
ここまでできた時点で、アプリケーションを実行してみましょう。Mac OS XのGoogle App Engine Launcherでは「Run」ボタンを押すだけで、専用のWebサーバが起動してブラウザからアプリケーションにアクセス可能になります(Windows/Linuxではコマンドラインからスクリプトを実行する必要があります)。
GAEのSDKは本番環境と同じアプリケーションの実行環境をエミュレートしていて、ユーザー認証などを含めてローカルでの動作チェックが可能になっています。
Ruby on Railsなどと同様に、ローカル環境のセットアップが不要ですぐチェックできるので、開発者にとって作業しやすくなっています。
ローカル環境でテストを行った後は、アプリケーションのデプロイを行います。これによって、開発したアプリケーションがGAEのドメインで実際に公開されるようになります。
従来、アプリケーションのデプロイは本番サーバのセットアップや構成ファイルのアップロードなど面倒で気を使うものでしたが、GAEではボタン操作のみ(Windows/Linuxではコマンドライン操作)でアップロードが完了し、そのほかには何もしなくても最新のコードを使ってサービスが行えます。
デプロイが完了すると、URL“http://アプリケーションID.appspot.com/”で実際にアプリケーションにアクセスできるようになります(ドメインに関しては、後から独自ドメインを使用するように変更することもできます)。
Users APIを利用することで、現在サービスにアクセスしているユーザーの情報を管理することができます。認証情報にはGoogleアカウントの登録情報が利用されます。面倒なセッション管理を実装する必要がないうえに、アカウント情報そのものをグーグルに依存することで、個人情報の管理コストを減らすこともできます。
Users APIを利用するためには google.appengine.api からusersパッケージを読み込み、users.get_current_user()でUserオブジェクトを取得します。
from google.appengine.api import users user = users.get_current_user() if not user: # ユーザーはログインしていない else: print "Hello, %s!" % user.nickname()
Userオブジェクトには、ディスプレイ用のユーザー名を取得する nickname() メソッドとメールアドレスを取得する email() メソッドがあります。
注意が必要なのは、ここで取得したメールアドレスの情報は後にサイトの外で変更される可能性があるということです。このため、GAEのデータベースに記録されたユーザー情報と現在のユーザーのマッチングを行うためには、UserProperty形式で直接Userオブジェクトを保存し、クエリに使用します(詳細は、Userオブジェクト解説ページのNotes:を参照してください)。
ユーザーから投稿されたデータを保存してアプリケーションで利用するには、データベースとの連携が不可欠です。GAEではDatastore APIを使用することでデータベースへのアクセスをオブジェクト経由で行うことができます。
まずは、必要なデータベースの定義を行います。
from google.appengine.ext import db class Log(db.Model): author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True)
db.Modelを継承してデータオブジェクトクラスを定義します。UserProperty, StringProperty, DateTimePropertyは、それぞれユーザー情報、文字列情報、日付情報を格納するカラムの属性です。
この定義が書かれたハンドラが実行されると、GAEは自動的にデータベースにテーブルとカラムを作成し、上記のオブジェクトからアクセス可能な状態にします。
試しに、SDK環境のコンソール画面をチェックするとデータが定義されている様子が分かります。
コンソール画面では、実際に格納されたデータを閲覧したり、新規作成、編集、削除などを行うことができます。
データベースができたので、今度はユーザーからの入力を格納してみます。フォームからの入力を処理するコードは、下記のようになります。
import cgi from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.ext import db class Log(db.Model): author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True) class LogHandler(webapp.RequestHandler): def post(self): log = Log() log.content = self.request.get('content') log.put() self.redirect('/log/list')
ハンドラpostを定義することで、フォームからのPOSTリクエストを処理できます。
ここでは、self.request.get('content')で“content”パラメータを取得してLogオブジェクトのcontentカラムにセットし、log.put()メソッドでデータベースへの反映を行っています。
格納されたデータを表示するハンドラコードは下記のようになります。ここではURL“/log/list”のハンドラを定義してデータベースから取得したLogデータの内容を一覧表示しています。
データを投稿するためのフォームも一緒に表示します。
class ListHandler(webapp.RequestHandler): def get(self): self.response.out.write('<html><body><ol>') logs = db.GqlQuery("SELECT * FROM Log ORDER BY date DESC LIMIT 10") for log in logs: self.response.out.write('<li>%s</li>' % cgi.escape(log.content.encode('utf-8'))) # Write the submission form and the footer of the page self.response.out.write(""" </ol><form action="/log" method="post"> <div><textarea name="content" rows="3" cols="60"> </textarea></div> <div><input type="submit" value="logme!"></div> </form> </body> </html>""")
GqlQuery()を使用して、日付順にソートしたデータを10件表示しています。
日本語のデータを扱う際に注意しないといけない点は、Pythonでは文字列はASCIIとして扱われているため表示を行う際にUTF-8などに変換をしてやる必要があることです。
logからcontentデータを取り出した後encode(‘utf-8’)で変換を行っています。
このハンドラの結果は下記のようになります。
GAEを使ったWebアプリケーション開発の実際について駆け足で解説してきましたが、およその雰囲気をつかんでいただけたでしょうか?
自分のマシンで動かしているWebアプリケーションが、面倒な運用の手間なしに公開されて多くのユーザーに使ってもらえる。GAEはそんなWebデベロッパーの夢を実現したサービスといえます。
一方で、バッチ処理のような必須機能が未実装である点や、GAE環境の利用量が500万PV程度までに制限されていて超過した際のパスがないことなど、正式版でないが故の課題もまだ残っています(いずれも、2008年10月5日現在)。
当面は、こうした課題が改善されるのを待ちながら、サービス開発をしてくことになるでしょう(500万PVの制限は、大抵の新規サービスにとってそれ自体が結構なハードルだとは思いますが)。
GAEチームの頑張りに期待しましょう。
記事の都合上、まだまだ紹介し切れていない機能が多くあります。URL Fetch APIを使った外部情報の取得やDjangoなどのWebフレームワークの利用などに興味のある方は、下記のリンクなどを参考にしてください。
なお、この記事で作成したアプリケーションは、「http://log-me.appspot.com/」でアクセスできます。徐々に機能を追加していく予定ですので、実例としてぜひご覧ください。
立薗理彦(たちぞの まさひこ)
1972年東京生まれ。1996年、慶應大学 環境情報学部卒。シャープで組み込み系のソフトウェアエンジニアとして働いた後、携帯電話メーカーのノキアで日本向け端末のリリースに携わる。
この頃、週末プロジェクトとしてiTunesでの再生履歴をネットで公開するサービス「音ログ」を開発。これをきっかけに、ウェブ業界への転身を決意してフリーに。
その後、音楽ニュースサイト「ナタリー」の立ち上げに関わり、2007年10月から技術担当取締役としてナターシャに参加。現在に至る。
最近の興味は、iPhoneでのアプリケーション開発。
趣味は、TVドラマ「Lost」を繰り返し見ること。全シーズンをすでに3回以上見ていて、ハワイでのロケ地ツアーにも2回参加。
著者つぶやき
http://mshk.tumblr.com/
http://twitter.com/mshk
http://friendfeed.com/rooms/sweet-tweet
Copyright © ITmedia, Inc. All Rights Reserved.