.. meta:: :keywords: web2py, framework, DAL, データベース抽象化レイヤ, テーブルリレーション .. _table_relation: テーブルのリレーション ====================== web2py のモデルでは、テーブルのリレーションの定義ができます。定義や利用方法について、リレーションのパターン毎に紹介していきます。 一対多のリレーション -------------------- .. image:: ../../images/web2py_dal/web2py_dal_025r.JPG 一対多のリレーションでは、参照側テーブルに参照用フィールドを設定します。設定をサンプルで示してみます。 :: db.define_table('person', Field('name'), format='%(name)s') db.define_table('dog', Field('owner_id', db.person), # フィールド設定でTableインスタンスを指定 Field('name')) リレーション設定は :ref:`class_field` クラスのコンストラクタにて、 :ref:`referenceキーワード ` でフィールドタイプを指定します。referenceキーワードの代わりに Tableインスタンスでも指定可能です。 リレーションを設定したテーブルから、参照先テーブルのフィールド値を取得するには :ref:`recursive_select` もしくは :ref:`join` を利用します。 また参照先のテーブルに formatオプションが設定されている場合は、represent属性などでアクセス可能です。formatオプションは :ref:`format` を 参照ください。 参考: `One to Many Relation `_ | `1対多のリレーション `_ .. _many_to_may_relation: 多対多のリレーション -------------------- .. image:: ../../images/web2py_dal/web2py_dal_026r.JPG DALで多対多のリレーションを実現する方法としては2つあります。一つはリレーション用テーブルを用意する方法です。 もう一つはlistフィールドを用いる方法です。 .. _relation_by_table: リレーション用テーブルを使用した多対多 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. image:: ../../images/web2py_dal/web2py_dal_027r.JPG リレーション用テーブルを使用する多対多は、RDBの正規化理論に従った手法です。パフォーマンスや保守性に問題ない限り、正規化理論に 従ってテーブルが切り分けられていきます。サンプルの定義を示します。 :: db.define_table('person', Field('name')) db.define_table('cat', Field('name')) db.define_table('ownership', Field('owner_id',db.person), Field('cat',db.cat)) ownershipテーブルがリレーションを管理します。テーブルの参照では一般的に :ref:`join` を使用します。 :: >>> rows = db((db.cat.id==db.ownership.cat) & (db.person.id==db.ownership.owner_id)).select() >>> for row in rows: ... print row.cat.name, row.person.name Motacesa Pacedada Pasamamo Socepopa Taducoto Cosotama 次のように記述することも可能です。 :ref:`class_set` インスタンスに追加した条件は AND(&) で設定されます。 :: >>> set = db((db.cat.id==db.ownership.cat) & (db.person.id==db.ownership.owner_id)) # Setインスタンス定義 >>> for row in set(db.person.name=='Socepopa').select(): # setに条件追加すると共にselectメソッド呼び出し ... print row.cat.name, row.person.name Pasamamo Socepopa 注意点としては、GAEではjoinをサポートしていません。このためjoinを使用せず検索するか、リストフィールドを使った多対多モデル を利用します。 参考: `Many to Many `_ | `多対多 `_ .. _list_relation: リストフィールドを使用した多対多 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. image:: ../../images/web2py_dal/web2py_dal_028r.JPG リストフィールドは、リスト型のデータを持つフィールドのことです。これは **NoSQL** であるGAEの Datastore が持つリスト型 フィールドと同じ機能です。web2pyはこのリストフィールドを Datastore のみならず、他のRDBに対しても実現しています。 リストフィールドについては :ref:`リストフィールド` も参照ください。 リストフィールドを使ったリレーションではフィールドタイプとして、 ``list:reference`` を使用します。また参照先のテーブルに format オプションを設定しておくと、比較的容易にアクセスが可能になります。これらの設定を行ったサンプルは次のようになります。 :: db.define_table('person', Field('name'),format='%(name)s') db.define_table('cat', Field('name'), Field('owner_id','list:reference person')) ``list:reference`` フィールドを使っったテーブルの読み出しは次のようになります。 :: >>> for row in db(db.cat).select(): ... print row.name, db.cat.owner_id.represent(row.owner_id) Datoposa Podatoso, Ducepapa Sataduda Podatoso, Pamadapa, Dudaduto Socedata Sapopadu, Dudaduto SQLFORM や SQLTABLE を利用した場合は、設定した formatオプションは自動で表示します。詳細は :ref:`format` を参照ください。 もしformatオプションを使わない場合、例えば次のようなコードになります。 :: >>> for row in db(db.cat).select(): ... for owner_id in row.owner_id: ... print row.name, db.person(owner_id).name Datoposa Ducepapa Datoposa Podatoso Sataduda Dudaduto リストフィールドに対し検索するには、 :meth:`~dal.Expression.contains` メソッドを使用します。 :: >>> for row in db(db.cat.owner_id.contains('2')).select(): # owner_idが2のcatデータを取得 ... print row.name, db.cat.owner_id.represent(row.owner_id) Datoposa Podatoso, Ducepapa Sataduda Podatoso, Pamadapa, Dudaduto containsメソッドはリストフィールドでは、個々の値の検索を行います。 参考: `Many to Many `_ | `多対多、list:、contains `_