以前に GAE for Python でJSONなデータのAJAX通信のやり方を紹介した。今回はもう少しだけステップアップして、GAEのModelをJSON変換する方法を紹介したいと思う。まだデータストアの基本を押さえていない人は先に
こちらで使い方を学んで欲しい。
さて、始めようか。
以下のサンプルは、画面からメッセージを投稿すると、サーバでGAEのデータストアへ登録されると同時にデータストアからメッセージ一覧を取得してJSON形式へ変換し、クライアント(ブラウザ)へ返すというものである。
まず重要となってくるのは、モデルをJSON形式にシリアライズための処理だ。
今回は
json.pyというクラスを利用させて貰った。その中身は以下の通りだ。
json.py# -*- coding: utf-8 -*-
"""Utility classes and methods for use with simplejson and appengine.
Provides both a specialized simplejson encoder, GqlEncoder, designed to simplify
encoding directly from GQL results to JSON. A helper function, encode, is also
provided to further simplify usage.
GqlEncoder: Adds support for GQL results and properties to simplejson.
encode(input): Direct method to encode GQL objects as JSON.
"""
import datetime
import simplejson
import time
from google.appengine.api import users
from google.appengine.ext import db
class GqlEncoder(simplejson.JSONEncoder):
"""Extends JSONEncoder to add support for GQL results and properties.
Adds support to simplejson JSONEncoders for GQL results and properties by
overriding JSONEncoder's default method.
"""
# TODO Improve coverage for all of App Engine's Property types.
def default(self, obj):
"""Tests the input object, obj, to encode as JSON."""
if hasattr(obj, '__json__'):
return getattr(obj, '__json__')()
if isinstance(obj, db.GqlQuery):
return list(obj)
elif isinstance(obj, db.Model):
properties = obj.properties().items()
output = {}
for field, value in properties:
output[field] = getattr(obj, field)
return output
elif isinstance(obj, datetime.datetime):
output = {}
fields = ['day', 'hour', 'microsecond', 'minute', 'month', 'second',
'year']
methods = ['ctime', 'isocalendar', 'isoformat', 'isoweekday',
'timetuple']
for field in fields:
output[field] = getattr(obj, field)
for method in methods:
output[method] = getattr(obj, method)()
output['epoch'] = time.mktime(obj.timetuple())
return output
elif isinstance(obj, time.struct_time):
return list(obj)
elif isinstance(obj, users.User):
output = {}
methods = ['nickname', 'email', 'auth_domain']
for method in methods:
output[method] = getattr(obj, method)()
return output
return simplejson.JSONEncoder.default(self, obj)
def encode(input):
"""Encode an input GQL object as JSON
Args:
input: A GQL object or DB property.
Returns:
A JSON string based on the input object.
Raises:
TypeError: Typically occurs when an input object contains an unsupported
type.
"""
return GqlEncoder().encode(input)
次にお馴染みの helloworld.py だ。
helloworld.py# -*- coding: utf-8 -*-
import cgi
import os
# json.py をインポート
import json
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
from google.appengine.ext import db
from django.utils import simplejson
import logging
#
# メッセージモデル
#
class Message(db.Model):
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
#
# メインハンドラ
#
class MainHandler(webapp.RequestHandler):
def get(self):
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, template_values))
#
# メッセージ一覧をJSONで返すハンドラ
#
class OutputJSON(webapp.RequestHandler):
def get(self):
# メッセージをデータストアへ登録
message = Message()
message.content = self.request.get('content')
message.put()
# データストアから最新10件のメッセージ一覧を取得
query = Message.all().order('-date')
results = query.fetch(10)
# メッセージ一覧をJSON変換してクライアントへ返す
json_results = json.encode(results)
self.response.out.write(cgi.escape(unicode(json_results, 'UTF-8')))
application = webapp.WSGIApplication([('/', MainHandler),
('/OutputJSON', OutputJSON)],
debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
そして、画面HTMLは以下の通り。
index.html<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/>
<title>GAE/P+jQueryでJSONをAJAX(ModelをJSONへ変換編)</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function() {
$("#btn").click(function() {
// JSONレスポンスを返すURL
url = "http://localhost:8080/OutputJSON";
// リクエストパラメータ作成
req = {
"content" : $("#content").val(),
"callback" : "?" /* 他ドメインにリクエストする場合には必要 */
};
// AJAX
$.getJSON(url, req, callback);
});
/* コールバック関数 */
var callback = function(json){
$("#res").text("");
$.each(json, function(i, item) {
message = '投稿日時: '
+ formatDate(new Date(this.date['isoformat']))
+ '<br/>コメント: ' + this.content;
+ '<hr/>';
$("#res").append($('<p/>').html(message));
});
};
// UTC → JST
function utc2jst(utc) {
// JST = UTC + 9H
return new Date(utc.getTime() + 9*60*60*1000);
}
function formatDate(date) {
// GAEでは日付をUTCで保持しているのでJSTへ変換する
jst = utc2jst(date);
year = jst.getYear();
month = jst.getMonth() + 1;
day = jst.getDate();
hour = jst.getHours();
minute = jst.getMinutes();
second = jst.getSeconds();
if (year < 2000) { year += 1900; }
if (month < 10) { month = '0'+month; }
if (day < 10) { day = '0'+day; }
if (hour < 10) { hour = '0'+hour; }
if (minute < 10) { minute = '0'+minute; }
if (second < 10) { second = '0'+second; }
return String(year)
+ '/'
+ month + '/'
+ day + ' '
+ hour + ':'
+ minute + ':'
+ second;
}
});
</script>
</head>
<body>
<p>リクエスト</p>
<textarea name="content" id="content"></textarea>
<button id="btn">送信</button>
<p>レスポンス</p>
<div id="res"></div>
</body>
</html>
重要となる Model から JSON への変換、以下の部分だ。
変数resultsは、Messageモデルのリストである。
json_results = json.encode(results)
それ以外で重要なところというと、index.html でJavascriptを使って日付を UST から JST(日本時間) へ変換しているところくらいかな。
GAEでは日付をUSTで保持しているので注意が必要だ。
ちなみに UST+9時間 が JST となる。
formatDate(new Date(this.date['isoformat']))
// UTC → JST
function utc2jst(utc) {
// JST = UTC + 9H
return new Date(utc.getTime() + 9*60*60*1000);
}
function formatDate(date) {
// GAEでは日付をUTCで保持しているのでJSTへ変換する
jst = utc2jst(date);
year = jst.getYear();
month = jst.getMonth() + 1;
day = jst.getDate();
hour = jst.getHours();
minute = jst.getMinutes();
second = jst.getSeconds();
if (year < 2000) { year += 1900; }
if (month < 10) { month = '0'+month; }
if (day < 10) { day = '0'+day; }
if (hour < 10) { hour = '0'+hour; }
if (minute < 10) { minute = '0'+minute; }
if (second < 10) { second = '0'+second; }
return String(year)
+ '/'
+ month + '/'
+ day + ' '
+ hour + ':'
+ minute + ':'
+ second;
}
以上だ。
ここはひとつポチっとよろしく。

Dan Sanderson
オライリージャパン
売り上げランキング: 92707