Amazon SimpleDBをPythonからちゃんと使う

なんかいつの間にかbotoが1.9bになっていてGentooのパッケージにもしっかり格納されている。そして1.9bではquery()メソッドは消されており、2007年時点での説明書は全く役にたたない。amazonaws.comというサイトには、API一覧っぽいのが掲載されているが、やはり説明されている部分は限られている。

こういう時はソースを読むんですよ。

ということでboto.sdb.db.modelあたりを読んでみた。こういうことらしい。

(1) SimpleDBに格納したいクラスはboto.sdb.db.Modelを継承させる。使える型はboto.sdb.db.property.*に定義されている。

from boto.sdb.db import Model
from boto.sdb.db.property import *
...
class TestDB(Model):
    f_name = StringProperty()
    l_name = StringProperty()

(2) そのクラスを格納するDB(RDBでいうとテーブルに相当)を/etc/boto.cfgに記述。実体はSimpleDBでいうDomain。

[DB_TestDB]
db_name = testdb

(3) 当然、そのドメインはなんらかの形で作っておく必要がある。

import boto
conn = boto.connect_sdb()
domain = conn.create_domain("testdb")

簡単なコードを書いてみた。

#! /usr/bin/env python

import boto
from boto.sdb.db.model import Model
from boto.sdb.db.property import *
from datetime import datetime
import time

class TestDB(Model):
    f_name = StringProperty()
    l_name = StringProperty()
    registered_datetime = DateTimeProperty()

def main():
    conn = boto.connect_sdb()
    conn.create_domain('testdb')

    t = TestDB()
    t.f_name = "Masayang"
    t.l_name = "Nakamura"
    t.registered_datetime = datetime.now()
    t.put() #これで書き込める

    decay = 0
    decay_max = 10
    success = False

    while success == False:
        result = TestDB.get_by_id(t.id)
        if result: # Eventually Consistentなので直後は読めないことがある
            print result.f_name
            print result.l_name
            print result.registered_datetime
            success = True
        else: #読めなかった場合は読めるまでリトライする
            if decay < decay_max:
                decay += 1
                time.sleep(decay)
            else: #ダメなら諦める
                success = True # 正しくは例外を上げるべきなんだろうけど...

if __name__ == "__main__":
    main()

このやり方なら、各種Webフレームワークのバックエンドとしても使えそうだ。boto.sdb.db.modelを読む限りReferenceも使えるようなので、関連しあったクラスの格納にも使えることであろう。

おまけ

SimpleDBにはこんな感じで格納されている。

>>> import boto
>>> conn = boto.connect_sdb()
>>> dom = conn.get_domain('testdb')
>>> rs = dom.select("SELECT * FROM testdb")
>>> rs = dom.select("SELECT * FROM testdb")
>>> for r in rs:
...     print r.name
...     print r
...
ab1a342a-b8c4-439c-b0dd-010062d194f2
{u'__module__': u'__main__', u'__lineage__': u'object.Model.TestDB', u'l_name': u'Nakamura', u'f_name': u'Masayang', u'registered_datetime': u'2010-08-02T13:07:05Z', u'__type__': u'TestDB'}

→SimpleDBは各要素をUTF8文字列で格納する。botoがPythonの型と文字列との変換を引き受けてくれる。各レコードのキー(simpleDBでいうname)はbotoが勝手につけてくれてるね。便利。