SQLObjectでリレーションを表現するにはいくつかの方法がある。なかでもSQLMultipleJoin()が面白いのでちょっと紹介。
TurboGearsのtg-admin shellを使っているので,importなどは省略。手元の環境にはIPythonがインストールされているので,シェルの出力がちょっと変わっている。
まず,SQLObject由来のクラスを定義する。mdl1 1 -- n mdl2という,一対多のリレーションを張っています。
In [1]:class mdl1(SQLObject): ...: row1 = IntCol(default=0) ...: mdl2 = ForeignKey('mdl2') ...: ...: In [2]:class mdl2(SQLObject): ...: row2 = IntCol(default=0) ...: mdl1 = SQLMultipleJoin('mdl1')
テーブルを作成。
In [5]:mdl1.createTable() In [6]:mdl2.createTable()
mdl2クラスのインスタンスを生成。
In [7]:m2 = mdl2(row2=1)
mdl1クラスのインスタンスを生成。この際,引数に紐づけたいmdl2インスタンスのIDを渡すのがポイント。
In [9]:for c in range(10): ...: mdl1(row1=c, mdl2ID=m2.id) ...:
さて,これで,mdl2のクラスインスタンスであるところのm2を使って,リレーションしたmdl1インスタンスにアクセスできる。リレーションしたmdl1は,mdl2にアトリビュートとして定義してあるので,これを表示してみる。「MultipleJoin」という,「SQL」の付かないアトリビュートとしてリレーションを定義すると,リストが返ってくるが...。
In [10]:m2.mdl1 Out[10]:<SelectResults at 260b790>
「SQLMultipleJoin」としてリレーションを定義するとSelectResultsオブジェクトが返ってくる。SelectResultオブジェクトは,イテレーション可能オブジェクトである。リスト内包表記を使うと,リレーションしているデータを取り出すことが出来る。
In [11]:[ x for x in m2.mdl1 ] Out[11]: [<mdl1 1 row1=0 mdl2ID=1>, <mdl1 2 row1=1 mdl2ID=1>, <mdl1 3 row1=2 mdl2ID=1>, (以下続く)
SQLMultipleJoinとして定義したリレーションでは,filter()関数が使える。リレーションにフィルタを掛けられるわけだ。「mdl1.q.row1 > 2」という部分がwhere句に相当する。クラス名から「q」というアトリビュートを挟み,条件として指定したい列を指定し,比較演算子を使った式を記述する。キーワード引数(a=1みたいな)を使ったなんちゃって条件指定ではないところが,質実剛健を目指す姿勢のあらわれとして好感が持てる。多分演算子をオーバーライドしてるんだろうな。
In [14]:[ x for x in m2.mdl1.filter(mdl1.q.row1 > 2) ] Out[14]: [<mdl1 4 row1=3 mdl2ID=1>, <mdl1 5 row1=4 mdl2ID=1>, <mdl1 6 row1=5 mdl2ID=1>,
複合条件を指定するときには,AND()関数などを使う。他に「OR()」「JOIN()」などがある。
In [15]:[ x for x in m2.mdl1.filter(AND(mdl1.q.row1 > 2, mdl1.q.row1 < 5)) ] Out[15]:[<mdl1 4 row1=3 mdl2ID=1>, <mdl1 5 row1=4 mdl2ID=1>]
SelectResultsオブジェクトは面白くて,文字列として評価するとSQLを返してくる。SQLObjectが発行するSQLはSQLBuilderというパッケージで作られている。SQLBuilderが発行するSQLをそのまま返しているわけだ。
In [17]:str(m2.mdl1) Out[17]:'SELECT mdl1.id, mdl1.row1, mdl1.mdl2_id FROM mdl1 WHERE ((mdl1.mdl2_id) = (1))'
filter()メソッドを使うとこんなかんじ。
In [21]:str(m2.mdl1.filter(AND(mdl1.q.row1>1, mdl1.q.row1<5))) Out[21]:'SELECT mdl1.id, mdl1.row1, mdl1.mdl2_id FROM mdl1 WHERE (((mdl1.mdl2_id) = (1)) AND (((mdl1.row1) > (1)) AND ((mdl1.row1) < (5))))'
と,いうように,SQLObjectを使うとデータベースをひたすらPythonicに扱えるわけである。