GAE/PyでLow Level API Keyオブジェクトを触ってみる
Slim3本を読んで「PythonでもデータストアのLow Level APIを触ってみたいなー」と思った。うちのMacはメモリが少ないのでEclipseを起動したくないのです。
オープンソース徹底活用Slim3onGoogleAppEngineforJava
- 作者: ひがやすを,小川信一
- 出版社/メーカー: 秀和システム
- 発売日: 2010/07/30
- メディア: 単行本
- 購入: 12人 クリック: 462回
- この商品を含むブログ (36件) を見る
Slim3本に沿って、まずはKeyオブジェクトから触ってみる。KeyというのはGoogle App Engineのエンティティを一意にする識別子。リレーショナルデータベースでいうところのプライマリーキーのようなもの。
BigTable全体の中で一意になる必要があるため、キーの中に
- アプリケーションID
- 親キー
- kind(テーブル名みたいなもの)
- ID or キー名
の情報を持っている。
Keyの動作確認コード
def test_idkey(self): key = Key.from_path('mykind', 1) print `key` def test_namedkey(self): key = Key.from_path('mykind', 'name') print `key`
Keyクラスの「from_path」関数でKeyを生成する。パラメータには「kindとid」または「kindとキー名」を渡す。idは数値でキー名は文字列。
出力
datastore_types.Key.from_path(u'mykind', 1, _app=u'testbed-test') datastore_types.Key.from_path(u'mykind', u'name', _app=u'testbed-test')
親子関係をもつキー
キーを作成する時に親キーを指定することができる。これでキーの親子関係ができる。
テスト
def test_childkey(self): parent = Key.from_path('parentkind', 1) child = Key.from_path('childkind', 1, parent=parent) grandchild = Key.from_path('grandchildkind', 1, parent=child) print `parent` print `child` print `grandchild`
出力
datastore_types.Key.from_path(u'parentkind', 1, _app=u'testbed-test') datastore_types.Key.from_path(u'parentkind', 1, u'childkind', 1, _app=u'testbed-test') datastore_types.Key.from_path(u'parentkind', 1, u'childkind', 1, u'grandchildkind', 1, _app=u'testbed-test')
GAE/Pyでユニットテスト
GAE/Pythonでユニットテストをするための準備
テスト自動化というより、dev_appserverを立ち上げずにLow Level APIの動作を見るのが目的。
Kay frameworkでのテスト
Kay frameworkを使ってるならアプリケーションフォルダに「tests」フォルダを作ってテストコードを保存して以下のコマンドを実行すればテストできる。かんたん!
python manage.py test
プレーンな環境でのテスト
フレームワークなしの環境だとちょっとめんどい。以下のリンクを見ながらやってみる。
unittest2パッケージをインストール
まずはユニットテスト用のパッケージ「unittest2」をインストールします。
wget http://pypi.python.org/packages/source/u/unittest2/unittest2-0.5.1.tar.gz tar xvzf http://pypi.python.org/packages/source/u/unittest2/unittest2-0.5.1.tar.gz cd unittest2-0.5.1 sudo python setup.py install
テストランナーのスクリプト
次にテストを実行するスクリプトを作成して適当なフォルダに保存。私はホームディレクトリの直下に保存しました。
rungaetest.py
#!/usr/bin/env python import optparse import sys # Install the Python unittest2 package before you run this script. import unittest2 USAGE = """%prog SDK_PATH TEST_PATH Run unit tests for App Engine apps. SDK_PATH Path to the SDK installation TEST_PATH Path to package containing test modules""" def main(sdk_path, test_path): sys.path.insert(0, sdk_path) import dev_appserver dev_appserver.fix_sys_path() suite = unittest2.loader.TestLoader().discover(test_path) unittest2.TextTestRunner(verbosity=2).run(suite) if __name__ == '__main__': parser = optparse.OptionParser(USAGE) options, args = parser.parse_args() if len(args) != 2: print 'Error: Exactly 2 arguments required.' parser.print_help() sys.exit(1) SDK_PATH = args[0] TEST_PATH = args[1] main(SDK_PATH, TEST_PATH)
直接実行できるようにします。
chmod 755 ~/.rungaetest.py
テストコードの例
テストコードを作成。setupとteardownで何か色々やっていますが、まだ詳しいことはわかっていません。
# testLLApi.py import unittest from google.appengine.ext import testbed from google.appengine.api.datastore import Get, Put, Entity class LLApiTestCase(unittest.TestCase): def setUp(self): self.testbed = testbed.Testbed() self.testbed.setup_env(app_id='ishida-appengine-py') self.testbed.activate() self.testbed.init_datastore_v3_stub() def tearDown(self): self.testbed.deactivate() def testPutAndGet(self): entity = Entity(kind='person') entity['name'] = 'ishida' entity['age'] = 31 key = Put(entity) stored = Get(key) self.assertEqual('ishida', stored['name']) self.assertEqual(31, stored['age'])
テストを実行
~/testgaepython.py /usr/local/google_appengine ./tests
1つめのパラメータはGoogle App Engineをインストールしたディレクトリのパス。2つめはテストコードが保存されたディレクトリのパス。
こんな感じで出力されます。↓
makoto@Mac-mini% ~/rungaetest.py /usr/local/google_appengine ./tests [/Users/makoto/work/projects/gae/ishida-makot-py] testPutAndGet (testLLApi.LLApiTestCase) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.005s OK
GAE/PyでデータストアのLow Level Apiを直接呼んでみる
インターネットでGAE/PyのデータストアLow Level APIを触ってるようなサンプルを探したけど見当たらなかった。しょうがないのでソースを読んでみたらそれっぽいものを見つけたので動かしてみた。
# testLLApi.py import unittest from google.appengine.ext import testbed from google.appengine.api.datastore import Get, Put, Entity from google.appengine.api.datastore_types import Key class LLApiTestCase(unittest.TestCase): def setUp(self): self.testbed = testbed.Testbed() self.testbed.setup_env(app_id='ishida-appengine-py') self.testbed.activate() self.testbed.init_datastore_v3_stub() def tearDown(self): self.testbed.deactivate() def testPutAndGet(self): entity = Entity(kind='person') entity['name'] = 'ishida' entity['age'] = 31 key = Put(entity) stored = Get(key) self.assertEqual('ishida', stored['name']) self.assertEqual(31, stored['age'])
眠いので詳しいことはまた今度書く。きっと書く。
GAE/Pyでの日付型
Google App Engineで現在の日付時刻を取得するとUTC時刻が返ってくる。
しかもこの日付にはタイムゾーン情報が入ってない。
from datetime import datetime now = datetime.now() now.strftime('%Y/%m/%d %H:%M:%S%z') # '2011/04/03 15:01:22' # ↑実際の日本時間より9時間前の時刻 # タイムゾーンなし
dateutilとかを使ってタイムゾーンを指定してあげれば、日本時間を取得することができる。
from datetime import datetime from dateutil.tz jst = dateutil.tz.gettz('Asia/Tokyo') now = datetime.now(jst) now.strftime('%Y/%m/%d %H:%M:%S%z') # '2011/04/04 00:05:49+0900'
kay frameworkを使ってるとこんな感じ。
from datetime import datetime from kay.utils jst = kay.utils.get_timezone(settings.DEFAULT_TIMEZONE) now = datetime.now(jst) now.strftime('%Y/%m/%d %H:%M:%S%z') # '2011/04/04 00:11:13+0900'
データストアにはタイムゾーン情報を保存できない
上ので日本時間での現在時刻を取得することはできたけど、これをデータストアに保存するとタイムゾーン情報が消えてしまう。
正確に言うと、タイムゾーン情報がついた日時データは、UTC時刻に変換された上で、タイムゾーン情報のない状態で保存される。
now = datetime.now(jst)) ishida = Person(updated = now) # '2011/04/04 00:11:13+0900' # 日本時間。タイムゾーンあり。 ishida.put() ishida = Person.get_by_id(1) ishida.updated # '2011/04/03 15:11:13' # UTC。タイムゾーンなし。
日本時間とUTCが混在するとプログラムはとてもややこしくなる。同じUTCでもタイムゾーンを持つ日付と持たない日付が混在したりするともう手が付けられない。
例えば、タイムゾーンありの日付とタイムゾーンなしの日付は比較することができない。比較しようとすると以下のエラーが発生する。
can't compare offset-naive and offset-aware datetimes
日記はじめました
なにを書こうかな