many-to-manyマッピング使用時の注意

ひたすら試すのだ。
して、SQLAlchemyのウリのひとつとして、高性能なone-to-manyやmany-to-manyマッピングがあります。
へたなマッピング実装だと、n:mで、n+1回クエリ投げちゃったりして大変なのですが、
とりあえずそのへんは、定義時にlazyオプションで設定できます。
lazy=Trueなら、実際にそのプロパティにアクセスしたときに取得。
lazy=Falseなら、親オブジェクト取得の際にJoinしていっしょにとってきてくれる。
ここで、many-to-manyのときの注意。
両端のオプションでlazy=Falseとするとなんかクエリがおかしいです。

以下のようにマッピングするとー

label_mapper = mapper(Label, label_table,
properties=dict(
people=relation(Person, secondary=label_person_table, lazy=False)
))


person_mapper = mapper(Person, person_table,
properties=dict(
labels=relation(Label, secondary=label_person_table, lazy=False)
))

こーんな愉快なクエリになります。

SELECT person.person_id AS person_person_id, person_aa3e.person_id AS person_aa3e_person_id, person_aa3e.name AS person_aa3e_name, label_762f.label_id AS label_762f_label_id, label_762f.name AS label_762f_name, person.name AS person_name
FROM person
LEFT OUTER JOIN label_person label_person_82b7
ON person.person_id = label_person_82b7.person_id
LEFT OUTER JOIN label label_762f
ON label_762f.label_id = label_person_82b7.label_id
LEFT OUTER JOIN label_person label_person_2e07
ON label_762f.label_id = label_person_2e07.label_id
LEFT OUTER JOIN person person_aa3e
ON person_aa3e.person_id = label_person_2e07.person_id
ORDER BY person.person_id, label_person_82b7.label_id, label_person_2e07.label_id
;

personからたどったlabelでpersonへのバックリファレンスあるわけで。
そのバックリファレンス分も結合してしまう。
さらに、そのバックリファレンスで結合したpersonのlabelsプロパティ分でもう一回結合。
そして、そのlabelsに対するpersonへの結合が…
循環してます。めちゃ増えます。
ということになってしまうので、両端でlazy=Falseオプションを使うのはやめときましょう。