このサイトについて

SQLObjectのSQLMultipleJoin

SQLObjectのSQLMultipleJoin

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に扱えるわけである。

2010-08-27 04:41