Reputation: 2538
Folks,
Want to make sure I understand this correctly. And please disregard the case for inheritance here (SentientBeing), trying to instead focus on polymorphic models in has_many :through relationships. That said, consider the following...
class Widget < ActiveRecord::Base
has_many :widget_groupings
has_many :people, :through => :widget_groupings, :source => :person, :conditions => "widget_groupings.grouper_type = 'Person'"
has_many :aliens, :through => :widget_groupings, :source => :alien, :conditions => "video_groupings.grouper_type = 'Alien'"
end
class Person < ActiveRecord::Base
has_many :widget_groupings, :as => grouper
has_many :widgets, :through => :widget_groupings
end
class Alien < ActiveRecord::Base
has_many :widget_groupings, :as => grouper
has_many :widgets, :through => :widget_groupings
end
class WidgetGrouping < ActiveRecord::Base
belongs_to :widget
belongs_to :grouper, :polymorphic => true
end
In a perfect world, I'd like to, given a Widget and a Person, do something like:
widget.people << my_person
However, when I do this, I've noticed the 'type' of the 'grouper' is always null in widget_groupings. However, if I to something like the following:
widget.widget_groupings << WidgetGrouping.new({:widget => self, :person => my_person})
Then all works as I would have normally expected. I don't think I've ever seen this occur with non polymorphic associations and just wanted to know if this was something specific to this use case or if I'm potentially staring at a bug.
Thanks for any help!
Upvotes: 117
Views: 22002
Reputation: 13056
For example: locations
have many trains
, copters
, trucks
, ships
. And trains
, copters
, trucks
, ships
have many locations
.
Different locations
can have similar transports(store in moveable
polymorphic columns).
#db/migrations/create_moveable_locations.rb
class CreateMoveableLocations < ActiveRecord::Migration
def change
create_table :moveable_locations do |t|
t.references :moveable, polymorphic: true
t.references :location
t.timestamps
end
end
end
#app/models/moveable_location.rb
class MoveableLocation < ActiveRecord::Base
belongs_to :moveable, polymorphic: true
belongs_to :location
end
#app/models/location.rb
class Location < ActiveRecord::Base
has_many :moveable_locations, dependent: :destroy
has_many :trains, through: :moveable_locations, source: :moveable, source_type: 'Train'
has_many :copters, through: :moveable_locations, source: :moveable, source_type: 'Copter'
has_many :trucks, through: :moveable_locations, source: :moveable, source_type: 'Truck'
has_many :ships, through: :moveable_locations, source: :moveable, source_type: 'Ship'
end
#app/models/train.rb
class Train < ActiveRecord::Base
has_many :moveable_locations, as: :moveable, dependent: :destroy
has_many :locations, through: :moveable_locations
end
#app/models/copter.rb
class Copter < ActiveRecord::Base
has_many :moveable_locations, as: :moveable, dependent: :destroy
has_many :locations, through: :moveable_locations
end
#app/models/truck.rb
class Truck < ActiveRecord::Base
has_many :moveable_locations, as: :moveable, dependent: :destroy
has_many :locations, through: :moveable_locations
end
#app/models/ship.rb
class Ship < ActiveRecord::Base
has_many :moveable_locations, as: :moveable, dependent: :destroy
has_many :locations, through: :moveable_locations
end
Using:
train1 = Train.create(title: 'Train1')
train2 = Train.create(title: 'Train2')
location1 = Location.create(title: 'Location1', train_ids: [train1.id, train2.id])
location2 = Location.create(title: 'Location2', trains: [train1, train2])
location3 = Location.create(title: 'Location3')
location3.train_ids << [train1., train2.id]
location4 = Location.create(title: 'Location34')
location4.trains << [train1, train2]
copter1 = Copter.create(title: 'Copter1', location_ids: [location1.id, location2.id]
copter2 = Copter.create(title: 'Copter1')
copter2.location_ids << [location1.id, location2.id, location3.id]
Upvotes: 0
Reputation: 23450
There is a known issue with Rails 3.1.1 that breaks this functionality. If you are having this problem first try upgrading, it's been fixed in 3.1.2
You're so close. The problem is you're misusing the :source option. :source should points to the polymorphic belongs_to relationship. Then all you need to do is specify :source_type for the relationship you're trying to define.
This fix to the Widget model should allow you do exactly what you're looking for.
class Widget < ActiveRecord::Base
has_many :widget_groupings
has_many :people, :through => :widget_groupings, :source => :grouper, :source_type => 'Person'
has_many :aliens, :through => :widget_groupings, :source => :grouper, :source_type => 'Alien'
end
Upvotes: 163
Reputation: 185
As mentioned above, this doesn't work with rails 3.1.1 due to a bug on :source, but it's fixed in Rails 3.1.2
Upvotes: 3
Reputation: 1121
has many :through and polymorphic don't work together. If you try to access them directly, it should throw an error. If i am not mistaken, you have to hand write widget.people and the push routine.
I don't think it is a bug, it is just something which hasn't been implemented yet. I would imagine we see it in the feature, because everyone has a case in which they could use it.
Upvotes: -4