2011年09月06日

GAE/Pでmemcacheを利用してデータアクセスを高速化。

GAE for Python で memcache を利用する方法を紹介する。memcacheとは早い話がキャッシュのことで、datastoreよりも高速に動作する。

以下はそのサンプルである。

お馴染みのゲストブックアプリだが、今回は未入力状態で投稿ボタンが押された場合は表示の更新と見なし、キャッシュから書き込み一覧を取得・表示する。もしキャッシュに書き込み一覧が無ければ、その時はデータストアから取得し、キャッシュに再登録するという流れである。言わずもがな、書き込みがあった場合はデータストアへ登録すると同時にキャッシュもクリアしておく。

<html>
<body>
{% for greeting in greetings %}
<p>{{ greeting.date }}<br/>
{{ greeting.content|escape }}</p>
{% endfor %}
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>

# -*- coding: utf-8 -*-
import cgi
import os
import logging
from google.appengine.api import users
from google.appengine.api import memcache
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

class Greeting(db.Model):
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
def get(self):
# memcacheから取得
greetings = memcache.get("greetings")
# memcacheに無ければDatastoreから取得
if greetings is None:
greetings_query = Greeting.all().order('-date')
greetings = greetings_query.fetch(10)
logging.info(u"datastoreから取得。")
# memcacheに格納(有効期限は600秒)
memcache.add("greetings", greetings, 600)
logging.info(u"memcacheへ保存。")
else:
logging.info(u"memcacheを利用しました。")

template_values = {
'greetings': greetings
}

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):
# 入力があった場合のみdatastoreへ保存し、
# memcacheをクリアします。
if self.request.get('content'):
greeting = Greeting()
greeting.content = self.request.get('content')
greeting.put()
logging.info(u"datastoreへ保存。")
memcache.delete("greetings")
logging.info(u"memcacheからクリア。")
self.redirect('/')

application = webapp.WSGIApplication([
('/', MainPage),
('/sign', Guestbook)],
debug=True)

def main():
logging.getLogger().setLevel(logging.DEBUG)
run_wsgi_app(application)

if __name__ == "__main__":
main()


重要な部分だけを抜粋すると以下のとおりだ。
from google.appengine.api import memcache

# memcacheへ登録
memcache.add("greetings", greetings, 600)
# memcacheから取得
greetings = memcache.get("greetings")
# memcacheから削除
memcache.delete("greetings")


■追記 2011-11-10
memcache.add は、値が既に存在している場合には上書きをしない。
上書きさせたい場合は memcache.set を利用する。


Memcacheの詳細は、本家の「Memcache の使用方法」を参照すべし。


ここはひとつポチっとよろしく。
人気ブログランキングへ

エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 4805


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 40082



posted by 寄り道退屈男 at 15:20 | Comment(0) | TrackBack(0) | GAE for Python

2011年06月23日

PythonでJST日付をUTC(GMT)に変換する。

以前はUTC日付をJST日付へ変換する方法を紹介したが、今度はその逆、JST日付をUTCへ変換する方法の紹介だ。

簡単に実現するためにPyTZという便利なモジュールを利用する。俺はとりあえずpytz-2006p.zipというファイルを落とした。解凍するとpytzというフォルダが現れるので、GAEアプリなどで利用したい場合は、そのフォルダごとGAEのプロジェクトフォルダ直下へコピーすれば良い。

使い方は以下の通りだ。
import pytz
from datetime import datetime

# ネイティブ日付を取得
native_dt = datetime.strptime('2011-06-23 17:12:00', '%Y-%m-%d %H:%M:%S')
# ローカル日付を取得
local_dt = native_dt.replace(tzinfo=pytz.timezone('Asia/Tokyo'))
# UTC日付を取得
utc_dt = local_dt.astimezone(pytz.utc)

print str(native_dt)
print str(local_dt)
print str(utc_dt)


実行すると以下のように表示される。
2011-06-23 17:12:00
2011-06-23 17:12:00+09:00
2011-06-23 08:12:00+00:00



ここはひとつポチっとよろしく。
人気ブログランキングへ

エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 4805


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 40082



posted by 寄り道退屈男 at 17:22 | Comment(0) | TrackBack(0) | GAE for Python

2011年06月22日

BeautifulSoupオブジェクトを生成する前にHTMLを明示的にdecodeすべし。

以前にBeautifulSoupの使い方を軽く紹介したが、文字コードについて大切なポイントを書くのを忘れていた。

BeautifulSoupでHTML/XMLを読み込む時は、文字コードを明示的に指定しておくのが良いということだ。

例えば、取得先ページのHTML/XMLの文字コードが euc-jp なんかだった場合は、以下のようにすれば文字化けに悩まされなくて済むわけである。
utf8html = html.decode('euc-jp', 'replace')
bs = BeautifulSoup(utf8html)


ここはひとつポチっとよろしく。
人気ブログランキングへ

エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 4805


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 40082



posted by 寄り道退屈男 at 17:38 | Comment(0) | TrackBack(0) | GAE for Python

2011年06月21日

GAE/Pで詳細なエラーログ(トレース情報)をlogging出力する

どんなウェブアプリ開発においても例外・エラーをキャッチした時にloggingしておきたいことは多々ある。かと言って except: logging.error("エラーです") みたいな最悪なコーディングはしたくないものである。これの何が最悪かというと、完全に例外情報を隠蔽してしまっていることである。このログから運用者・開発者が知ることが出来るのは "エラーです" という残念なメッセージだけだ。これでは不親切にも程がある。

こういう時は traceback.format_exc() を使えば良い。

import traceback

try:
# 何らかの処理
except:
# 例外をキャッチ
logging.error(traceback.format_exc())


上記のようにコーディングすれば、例外時に詳細なエラー情報(トレースログ)がlogging出力され、エラー箇所を発見しやすくなる。

例外を握り潰したり改ざんしたりせず、成る可くそのまま例外を扱うのはアプリ開発の基本中の基本である。

ここはひとつポチっとよろしく。
人気ブログランキングへ

エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 4805


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 40082



posted by 寄り道退屈男 at 11:29 | Comment(0) | TrackBack(0) | GAE for Python

2011年06月17日

Pythonでオブジェクトのlistをソート・リバース・マージする方法。

Python初心者の俺が、オブジェクトのリストをソートしたりリバースしたりマージしたりする方法を知ったのでメモがてらに書いておこうと思う。前回はGAE/PでAOPする方法とか書いてたくせに、急に肩の力が抜けるエントリで恐縮である。

おっとその前に、まずはオブジェクトのリストではなく単純な文字列や数値のリストをソート・リバース・マージする方法から紹介しておこう。

# -*- coding: utf-8 -*-

# ソート
lst = ['apo', 'apa', 'baa']
lst.sort()
print lst

# リバース
lst = ['apo', 'apa', 'baa']
lst.reverse()
print lst

# マージ
from heapq import merge
lst1 = ['hoge', 'foo']
lst2 = ['baa', 'baz']
lst = list(merge(lst1, lst2))
print lst


これを実行するとこうなる。
['apa', 'apo', 'baa']
['baa', 'apa', 'apo']
['baa', 'baz', 'hoge', 'foo']


マージの時だけは from heapq import merge を使う必要がある。他の方法でもやれなくはないが、これを使うのが一番手っ取り早いしソースも綺麗だと思われる。

さて、いよいよオブジェクトのリストをソートまたはリバースする方法だ。ちょっとだけ敷居が上がるが、Python2.4以降だと import operator すれば非常に簡単に実現できる。

# -*- coding: utf-8 -*-
import operator

# 商品クラス
class Item:
def __init__(self, name, price):
self.name = name # 商品名
self.price = price # 価格
def __repr__(self):
return "Item(%s, %d)" % (self.name, self.price)

# まずは商品オブジェクトのリストを生成
lst = [Item('banana', 99),
Item('apple',60),
Item('kiwi',40)]

# 商品名でソート
lst.sort(key=operator.attrgetter('name'))
print lst

# 価格でソート
lst.sort(key=operator.attrgetter('price'))
print lst

# 価格でリバース
lst.sort(key=operator.attrgetter('price'), reverse=True)
print lst

# リストをマージ
from heapq import merge
lst2 = [Item('hoge', 11),
Item('foo',22)]
lst = list(merge(lst, lst2))
print lst


以下は実行結果だ。
[Item(apple, 60), Item(banana, 99), Item(kiwi, 40)]
[Item(kiwi, 40), Item(apple, 60), Item(banana, 99)]
[Item(banana, 99), Item(apple, 60), Item(kiwi, 40)]
[Item(banana, 99), Item(apple, 60), Item(kiwi, 40), Item(hoge, 11), Item(foo, 22)]


素晴らしくシンプルである。


ここはひとつポチっとよろしく。
人気ブログランキングへ

エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 4805


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 40082



posted by 寄り道退屈男 at 10:50 | Comment(0) | TrackBack(0) | GAE for Python