最初に、サンプルアプリの仕様を説明
概要:
・1からインクリメントしながら番号を表示し、3の倍数と3の付く数字のときに別の文字列を、5の倍数のときにも更に別の文字列を表示する。
・終点となる番号が設定できる。
・3の倍数と3のつく数字のときに読み上げる文字列を設定できる。
・5の倍数のときに読み上げる文字列を設定できる。
・設定した値はユーザ毎に永続化される。
・アプリケーション名は世界の○○とする。○○のところはユーザのnicknameを設定したい。
・formのvalidationについては、今回は行わない。(本当は行う必要があるので、次回以降のエントリで機能追加します。
最終的に作成したい画面イメージ
それではユーザ毎の設定値を保持するためのモデルを定義したいと思う。
ユーザ毎の設定値なのでとりあえずクラス名は、nabeatsuUserMasterとし、db.Modelを継承する。
継承することで、Google App EngineのDataStoreを利用して永続化するためのモデルと認識される。
あとは、サンプルのコメントで説明する。
#の行がコメント。
青色になっていると思う。
from google.appengine.ext import db
class nabeatsuUserMaster(db.Model):
author = db.UserProperty()
count = db.IntegerProperty()
date = db.DateTimeProperty(auto_now_add=True)
saidWhen3 = db.StringProperty()
saidWhen5 = db.StringProperty()
これで、とりあえず、nabeatsuUserMasterは完成です。
つづいて設定と読み上げを行うためのページを定義します。
ここで、DJangoのテンプレートを作成します。ファイル名は、なんでも良いのですが、とりあえずindex.htmlとします。
基本的な機能だけを利用していますので先に利用している機能を説明します。
{{ parameterName }}
で後で説明するwebのリクエストをハンドラで定義したパラメータを出力します。
{% 構文 %}
とすることである程度の構文が使えます。今回はfor value in valuesのみ利用していますので、
valuesからvalueを取り出して出力という処理を
{% for value in values %}
{{ value }}
{% endfor %}
といった感じで実装しています。
後で説明するリクエストハンドラから受け付けているパラメータ名の説明をしておきます。
userName=今ログインしているユーザのnickname
logoutUrl=ログアウトするためのUrlです。
count=読み上げる数字の終点です。
saidWhen3=3の倍数と3のつく数字のときに表示する文字列
saidWhen5=5の倍数のときに表示する文字列
outputValues=読み上げた文字列のリストです。
index.htmlの内容です。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="ja" xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>世界の{{ userName }}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="/nabeatsu/styles/styles.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="header">
世界の{{ userName }} <a href="{{ logoutUrl }}">ログオフ</a>
</div>
<form action="/nabeatsu/set" method="post">
<div>読み上げてほしい数:<input type="text" name="count" value="{{ count }}" tabindex="1" accesskey="c" /></div>
<div>3の倍数と3のつく数字のとき<input type="text" name="saidWhen3" value="{{ saidWhen3 }}" tabindex="2" accesskey="3" />になって、</div>
<div>5の倍数のとき<input type="text" name="saidWhen5" value="{{ saidWhen5 }}" tabindex="3" accesskey="5" />になります。</div>
<div><input type="submit" value="設定" tabindex="4" accesskey="s" /></div>
</form>
<div id="body" >
{% for value in outputValues %}
{{ value }} <br />
{% endfor %}
</div>
</body>
</html>
つづいて設定を更新するためのリクエストハンドラを作成します。
ここで、前もって作成しておいたnabeatsuUserMasterにデータを追加・更新する部分の作成します。
前段のテンプレートどおり、/nabeatsu/setに対応したハンドラとして、webapp.RequestHandlerを継承してUserMasterUpdateというクラスを作成します。
今回は、nabeatsuUserMasterと1対1で対応していますので、同じスクリプトファイルに記述されている前提です。
例によってサンプル中のコメントで説明します。
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api import users
class nabeatsuUserMaster(db.Model):
author = db.UserProperty()
count = db.IntegerProperty()
date = db.DateTimeProperty(auto_now_add=True)
saidWhen3 = db.StringProperty()
saidWhen5 = db.StringProperty()
class UserMasterUpdate(webapp.RequestHandler):
def post(self):
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
if self.request.get('count').count == 0:
self.redirect('/nabeatsu/')
return
Counts = nabeatsuUserMaster.gql("where author = :author",author=user)
if Counts.count() <= 0:
Count = nabeatsuUserMaster()
else:
Count = Counts[0]
Count.author = user
Count.count = int(self.request.get('count'))
Count.saidWhen3 = self.request.get('saidWhen3')
Count.saidWhen5 = self.request.get('saidWhen5')
Count.put()
self.redirect('/nabeatsu/')
つづいて、一番肝心なindex.htmlに対応したリクエストハンドラを作成します。
ここで、テンプレートであるindex.htmlに対応したリクエストハンドラを作成します。
このリクエストハンドラ内で、該当ユーザ毎のデータを持ってきて、読み上げた数字のリストを他のデータとともに、テンプレートに渡しています。
例によってサンプル内のコメントで説明します。
import wsgiref.handlers
import cgi
import os
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api import users
from google.appengine.ext.webapp import template
import masters
class MainPage(webapp.RequestHandler):
def nabeatsu(self,saidWhen3,saidWhen5):
def reader():
isAho = reader.value % 3 == 0 or str(reader.value).find('3') >= 0
isDog = reader.value % 5 == 0
outputValue = ''
if isAho:
outputValue += cgi.escape(saidWhen3.encode('UTF-8'))
if isDog:
outputValue += cgi.escape(saidWhen5.encode('UTF-8'))
if not isAho and not isDog:
outputValue = cgi.escape(str(reader.value))
reader.value += 1
return outputValue
reader.value= 1
return reader
def get(self):
user = users.get_current_user()
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
count = 40
saidWhen3 = u'あほ!'
saidWhen5 = u'わん!'
currentCount = masters.nabeatsuUserMaster.gql("where author = :author",author=user)
if currentCount.count() > 0:
count = currentCount[0].count
saidWhen3 = currentCount[0].saidWhen3
saidWhen5 = currentCount[0].saidWhen5
reader = self.nabeatsu(saidWhen3,saidWhen5)
outputValues = []
for i in xrange(1,count + 1,1):
outputValues.append(reader())
template_values = {
'userName' : user.nickname(),
'logoutUrl': cgi.escape(users.create_logout_url("/nabeatsu/")),
'count' : count,
'saidWhen3' : saidWhen3,
'saidWhen5' : saidWhen5,
'outputValues': outputValues,
}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, template_values))
def main():
application = webapp.WSGIApplication(
[('/nabeatsu/', MainPage),
('/nabeatsu/set', masters.UserMasterUpdate)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
最後にGoogle App Engineのアプリケーション定義をして終了です
上から、
アプリケーションの名前
バージョン
ランタイム(pythonしかないので固定)
apiのバージョン(いまのところ1)
handlersはurlとスクリプトの割り当て
/nabeatsu/とnabeatsu.pyを割り当て
/nabeatsu/setとnabeatsu.pyを割り当て
スタイルシートのパスを割り当て
application: nabeatsu
version: 1
runtime: python
api_version: 1
handlers:
- url: /nabeatsu/
script: nabeatsu.py
- url: /nabeatsu/set
script: nabeatsu.py
- url: /nabeatsu/styles/
static_dir: styles
以上で、完成です。
スタイルシートについては適当に設定してあげれば良いですが、一応サンプルとして添付しておきます。
#body
{
color:blue;
font-weight:bold;
height:200px;
overflow-y:scroll;
border:solid 1px black;
}
body
{
color:Black;
margin: auto;
FONT-FAMILY: "MS ゴシック";
FONT-SIZE: 9pt;
FONT-WEIGHT: normal;
LETTER-SPACING: normal;
TEXT-TRANSFORM: none;
background-color: #FFFFFF;
WORD-SPACING: normal;
}
とりあえず、簡単なサンプルですが、用意した機能の基本的な部分を利用しながら作成する手順については追えるのではないでしょうか。