読者です 読者をやめる 読者になる 読者になる

「作ればわかる! Google App Engine for Java プログラミング」本をPythonで書いてみる (1)

GoogleAppEngine Python

Google App EnginePythonができるようになりたかったので、良い参考書がないか探してみたところ、評判の良い本に出会った。

作ればわかる!Google App Engine for Javaプログラミング

作ればわかる!Google App Engine for Javaプログラミング


コード自体はJavaで書かれていたものの、説明がわかりやすい上に面白い題材ばかりなので、これをPythonで作ってみることにした。
(公式サポートサイトにもPythonの記載が一部あるため、利用できるだろうとも踏んだ)

■環境

Pythonで悩んだところと参考ページ

基本的な内容ばかりだけれど、自分が新しく入門した場合、こういうところでつまずくのかと改めて思った次第。

5.パッケージ

今回は不要だったのかもしれないが、「__init__.py」の空ファイルを用意。
とりあえず - Python入門(38) - パッケージ

6.C#の static みたいなメソッドは何を使えば良いのか?
  • クラスメソッド
  • スタティックメソッド

の2つあり。クラスに依存するのはクラスメソッド、依存しないのはスタティックメソッドとすればよいのかな?
参考:

7.プライベートなメンバの書き方

先頭に「__」(アンダースコア x2)をつける
blanket log - アクセス制御の抜け穴 (Python/Ruby編)


Google App Engineで悩んだところと参考ページ

1.フォームからのデータの受け取り

OpenService - Google App Engineを試す(1)

2.エラーのハンドリング

今回の場合はwebapp2を利用しているので、以下の2つの方法が取れる。

  1. app.yamlに、「error_handlers」セクションを追加
  2. webapp2の「handle_exception」メソッドをオーバーライド


error_handlersではエラーコードがサポートされているとの記載があったものの、サポートされていない場合(ロジックのバグ)はどうなるのか分からず。
実装してみたところ、ロジックのバグでもハンドリングしてくれる上、App EngineのLogにはエラーとして残っていた。
今回の場合、独自処理は不要だったので、前者を利用することにした。


参考:

3.webapp2のデバッグフラグの使い分け

開発環境ではスタックトレースを出して、本番環境では出さないようにするには、以下のwebapp2公式ページを参照。
ちなみに、上記6.のハンドリングをしていると、本番環境でもエラー画面が出たが、それで大丈夫なのかはわからない。
webapp2 - The WSGI application Debug flag

4.URLマッピング

Google Developersの公式サイトのヒント欄も参考になった。

5.cssファイルなどの静的ファイルの指定

Google Developers - Google App Engine 静的ファイルの使用

6.UnicodeDecodeErrorの発生

「'あいう'」と「u'えお'」を結合させた時に発生。先頭に「u」をつけることを忘れずに。
かせきのうさぎさん - 明後日の場所でUnicodeDecodeError


■フォルダ構成

css/
 +style.css
html/
 +index.html
 +index_post.html
 +error.html
__init__.py
app.yaml
index.py
sentence_helper.py

■ソース

画面遷移は「ドメイン」or「/index.html」でアクセスし、post後は「/submit」とした。
htmlファイルはテンプレートを利用(今回の場合、あまり意味が無い)。


なお、ソースコードのライセンスは本のライセンスに合わせて「Apache 2.0 ライセンス」に。
また、cssファイルは公式ページのモノそのままなので、省略。


GitHubへは、「9784798123028_GAE」という名称で作成。

app.yaml
application: <your application id>
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:

- url: /css
  static_dir: css
  
- url: /
  script: index.app
  
- url: /index\.html
  script: index.app

- url: /.*
  script: index.app

error_handlers:
  - file: html/error.html
index.py
#-*- coding: utf-8 -*-

import webapp2
from google.appengine.ext.webapp import template
import logging
import os

import sentence_helper


class IndexPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write(template.render('html/index.html', {}))


    def post(self):
        result = sentence_helper.SentenceHelper.make_sentence(self.request.get('when'),
                                                              self.request.get('where'),
                                                              self.request.get('who'),
                                                              self.request.get('what'))

        self.response.out.write(template.render('html/index_post.html', {'result' : result,}))


# 環境によるデバッグフラグの設定
# See http://webapp-improved.appspot.com/guide/app.html#debug-flag
debug = os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')

app = webapp2.WSGIApplication([('/', IndexPage),
                               ('/submit', IndexPage),
                               ('/index.html', IndexPage)], debug=debug)
sentence_helper.py
#-*- coding: utf-8 -*-

import random

class SentenceHelper(object):
    __whenList = [u'さっき', u'昨日', u'あれはもう3年も昔', u'紀元前', u'ジュラ紀']
    __whereList = [u'自宅で', u'近くの居酒屋で', u'世界のまんなかで', u'追い込み中のプロジェクトで', u'木星で']
    __whoList = [u'わたしが', u'あなたが', u'オヤジが', u'あこがれのアイドルが', u'神が']
    __whatList = [u'新しいソートアルゴリズムを発見した', u'イグノーベル賞を受賞した', u'悟りを開いた', u'こけた', u'十円ひろった']


    @classmethod
    def make_sentence(self, when, where, who, what):
        when = self.__choice(self.__whenList) if when == '' else when
        where = self.__choice(self.__whereList) if where == '' else where
        who = self.__choice(self.__whoList) if who == '' else who
        what = self.__choice(self.__whatList) if what == '' else what
	
        return when + where + who + what
	
	
    @staticmethod
    def __choice(lists):
        randomIndex = random.randint(0, len(lists) - 1)
        return lists[randomIndex]
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>イツドコデダレガナニシタ</title>

<link href="/css/style.css" rel="stylesheet" type="text/css" />

</head>
<body>
    <form action="/submit" method="POST">
        <h1>イツドコデダレガナニシタ</h1>
        
        <div class="box">
            <dl>
                <dt>いつ</dt>
                <dd>
                    <input type="text" name="when" />
                </dd>

                <dt>どこで</dt>
                <dd>
                    <input type="text" name="where" />
                </dd>

                <dt>だれが</dt>
                <dd>
                    <input type="text" name="who" />
                </dd>

                <dt>なにした</dt>
                <dd>
                    <input type="text" name="what" />
                </dd>
            </dl>
            
            <p><input type="submit" value="新しい文章を作成する" /></p>
        </div>
    </form>
</body>
</html>
index_post.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>イツドコデダレガナニシタ</title>

<link href="/css/style.css" rel="stylesheet" type="text/css" />

</head>
<body>
    <h1>イツドコデダレガナニシタ</h1>
    <p>{{ result }}</p>
</body>
</html>


***error.html
>|html|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>イツドコデダレガナニシタ</title>

<link href="/css/style.css" rel="stylesheet" type="text/css" />

</head>
<body>
    <form action="/index" method="POST">
        <h1>イツドコデダレガナニシタ</h1>
        <div class="box">
            <p>エラーが発生しました。</p>
            <p><a href="index">もう一度やり直してください</a></p>
        </div>
    </form>
</body>
</html>


今後も少しずつ進めていきたい。