2013年6月14日金曜日

[Rails3] has_many through で polymorphic な関連で相互に参照する

Rails3で has_many through でしかも polymorphic な関連を実装していて、polymorphicなモデル側からhas_manyを探せるけど、逆のやり方に苦戦したのでメモ

User モデルは複数の Clubに属すことができて、Clubもまた複数の Userを受け入れられるという感じ。

最初にやってた方法

Membership はpolymorphicなモデルにしていて、例えばClub以外にもFamilyとか他のグループでも同じモデルを使いまわしたかった。

データを作ってみる

rails consoleでデータを作成

user = User.create({ name: 'kozo' })
=> #<User id: 1, name: "kozo", created_at: "2013-06-14 03:14:59", updated_at: "2013-06-14 03:14:59">
club = Club.create({ name: 'football' })
=> #<Club id: 1, name: "football", created_at: "2013-06-14 03:14:50", updated_at: "2013-06-14 03:14:50">

club.memberships.create({ user_id: user.id })
=> #<Membership id: 1, user_id: 1, membable_id: 1, membable_type: "Club", created_at: "2013-06-14 03:15:19", updated_at: "2013-06-14 03:15:19">

Club -> Userの参照はうまくいく

こんな感じにClub側からUserを参照するのは簡単にできちゃう

club = Club.find(1)
club.members
# =>[#<User id: 1, name: "kozo", created_at: "2013-06-14 03:14:59", updated_at: "2013-06-14 03:14:59">]

User -> Clubの参照はできなかった・・・

user = User.find(1)
user.membered_clubs
# => ActiveRecord::HasManyThroughSourceAssociationNotFoundError: 
 Could not find the source association(s) :membable in model Membership.
 Try 'has_many :membered_clubs, :through => :memberships, :source => <name>'. Is it one of :user or :club?

ドキュメントにはsourceとsource_typeを使えばOKって書いてあるっぽい

ruby/rails/RailsGuidesをゆっくり和訳してみたよ/Active Record Associations - 株式会社ウサギィwiki

4.3.2.18 :source

:source オプションは has_many :through アソシエーションの元になるアソシエーション名を指定します。 元のアソシエーションの名前が自動的にアソシエーション名から推測できない場合にのみ、このオプションを使用する必要があります。

4.3.2.19 :source_type

:source_type オプションは polymorphic アソシエーションを通じて進める has_many :through アソシエーションの元になる型を指定します。

でもググったらすぐ出てきた

has_many :through - The other side of polymorphic :through associations

MembershipとUserに細かく書いてあげるといいっぽい

club = Club.find(1)
=> #<Club id: 1, name: "football", created_at: "2013-06-14 03:14:50", updated_at: "2013-06-14 03:14:50">
club.members
=> [#<User id: 1, name: "kozo", created_at: "2013-06-14 03:14:59", updated_at: "2013-06-14 03:14:59">]

user = User.find(1)
=> #<User id: 1, name: "kozo", created_at: "2013-06-14 03:14:59", updated_at: "2013-06-14 03:14:59">
user.membered_clubs
=> [#<Club id: 1, name: "football", created_at: "2013-06-14 03:14:50", updated_at: "2013-06-14 03:14:50">]

できた!

0 コメント:

コメントを投稿