Rails3で has_many through でしかも polymorphic な関連を実装していて、polymorphicなモデル側からhas_manyを探せるけど、逆のやり方に苦戦したのでメモ
User モデルは複数の Clubに属すことができて、Clubもまた複数の Userを受け入れられるという感じ。
最初にやってた方法
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Club < ActiveRecord::Base | |
attr_accessible :name | |
has_many :memberships, | |
as: :membable | |
has_many :members, | |
source: :user | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Membership < ActiveRecord::Base | |
attr_accessible :membable_id, :membable_type, :user_id | |
belongs_to :club, polymorphic: true | |
belongs_to :user | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class User < ActiveRecord::Base | |
attr_accessible :name | |
has_many :memberships, | |
dependent: :destroy | |
has_many :membered_clubs, | |
through: :memberships, | |
source: :membable, | |
source_type: 'Club' | |
end |
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に細かく書いてあげるといいっぽい
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Membership < ActiveRecord::Base | |
attr_accessible :membable_id, :membable_type, :user_id | |
belongs_to :user | |
belongs_to :membable, | |
polymorphic: true | |
belongs_to :club, | |
class_name: 'Club', | |
foreign_key: 'membable_id' | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class User < ActiveRecord::Base | |
attr_accessible :name | |
has_many :memberships, | |
dependent: :destroy | |
has_many :membered_clubs, | |
through: :memberships, | |
source: :club, | |
conditions: "memberships.membable_type = 'Club'" | |
end |
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 コメント:
コメントを投稿