Turbogears(SQLAlchemy)でテーブルを動的に読み込む
http://www.josw.net/blog/?p=1053
というわけで__new__の続きです。
Turbogearsでは、model.pyでテーブルとクラスを定義してそれらをマッピングしますね。
で、controller.pyでsession使ってmodel.Userみたいな感じでデータを読み出します。
ここまでは普通に紹介されてることですね。
では本題に移ります。
まず、同じ構造のテーブルが、時間が経つごとに名前を変えて増えていく、というシチュエーションを思い浮かべてください。
違うのはテーブル名だけなので、model.pyには以下のような定義があればよさそうな感じがします。
name = 'foo'
tablename = 'user_' + name
table_user = Table(tablename, metadata,
Column('id', Integer, primary_key=True),
Column('hitokoto', Unicode(400)),
)class TableUser(object):
def __init__(self, hitokoto):
self.hitokoto = hitokotomapper(TableUser, table_user)
これでとりあえずtg-admin sql createが通り、実際に動作させることができます。
しかしこれだけだと、user_fooというテーブル以外を扱おうとすると、それなりに労力がかかりそうです。
というわけでこいつを以下のように書き換えて、もう少し扱いやすくします。
username = 'foo'
class MetaTableUser(object):
def __new__(cls, name):
tablename = 'user_' + name
table_user = Table(tablename, metadata,
Column('id', Integer, primary_key=True),
Column('hitokoto', Unicode(400)),
)
return table_usertable_user = MetaTableUser(username)
class TableUser(object):
def __init__(self, hitokoto):
self.hitokoto = hitokotomapper(TableUser, table_user)
metadata.remove(table_user)
はい。ここで出てきました我らが__new__さん。関数返してます。
何のためにこういうことしてるかというと、普通の関数に初期値を与えて扱えるようにするためなのと、controller.pyに同じ内容を書かなくて済むようにです。メタ関数(適当言ってます)。
それから、最後の行はおまじないです。さらっと言ってますが、この1行のために1日ほどかかってます。
ではでは下準備も済んだので、controller.pyの記述に入って行きましょう。
とりあえずこんなかんじです。
@expose(template="alchemymytest.templates.userpage")
def user(self, username='anonymous'):
flash(("This is", username, "\'s page."))meta_table_user = model.MetaTableUser(username)
othermapper = mapper(model.MetaUser, meta_table_user, non_primary=True)
hitokotos = [hitokoto.hitokoto for hitokoto in session.query(othermapper)]
metadata.remove(meta_table_user)
return dict(username=username, hitokotos=hitokotos)
簡単に説明します。まず、http://localhost:8080/user/ というページを定義しました。
そして、そのページにはuser_hogeのhogeに当たる部分を引数として与えます。http://localhost:8080/user/hoge/ みたいな感じです。
次に、5行目は__new__さんがusernameのテーブル名を持ったTableオブジェクト(関数)を返してきてます。
そして7行目でマッピングするわけですが、そのままマッピングしちゃうとmodel.pyでマップしたものとかぶっちゃうので、別物としてマッピングします。
そして8行目でuser_usernameなテーブルを読み出していくというわけです。
そして9行目ですが、同じテーブルを定義しようとすると怒られるので、今定義したテーブルを削除してます。
では http://localhost:8080/user/ にアクセスしてみましょう。あ、テンプレートの準備がまだでしたね。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
py:extends="'master.kid'">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
<title>user page - ${username}</title>
</head>
<body>
<h1>${username}'s hitokoto</h1>
<ul>
<li py:for="hitokoto in hitokotos">
<${hitokoto}</a>
</li>
</ul>
</body>
</html>
これをuserpage.kidとして保存します。それから、ついでにuser_hogeとかでテーブルを何個か作って(user_anonymousは必須)、その中に適当なデータを格納しておいてください。
ここまでうまく行ってれば、user_anonymousに格納したデータがずらっとでてくるはずです。せっかくなので他のもみてみましょう。
さて、こんなところです。
眠さの限界なので考えずに書いてます。ごめんなさい。
わからないことあったらコメントで聞いてくださいね!
2008/4/2追記
本文に入れよう入れようと思って忘れてました。
別に、__new__を使わなくても出来ます。
こっちのほうが好きだったし、こういうやり方もあるよ的な感じです。