yesterday i faced a problem with my rails application, my models are as follows
1class Vehicle < ActiveRecord::Base
2 MOVABLES = [:car, :bus, :plane]
3 belongs_to :movable, polymorphic: true, dependent: :destroy, required: true, autosave: true
4 accepts_nested_attributes_for :movable
5end
1class Car < ActiveRecord::Base
2 has_one :vehicle, dependent: :destroy, as: :movable
3end
1class Bus < ActiveRecord::Base
2 has_one :vehicle, dependent: :destroy, as: :movable
3end
1class Plane < ActiveRecord::Base
2 has_one :vehicle, dependent: :destroy, as: :movable
3end
so, when I try to create a vehicle with nested attributes for the movable
it complains
with error “are you trying to build a polymorphic relation?”
1Vehicle.create! name: 'my car', movable_type: 'Car', movable_attributes: { color: red }
the previous code should create a vehicle with extended attributes of a car with color = red,
so i had to google first and guess what is the first thing to find?, Yup it is Stackoverflow
the highly rated solution didn’t work for me :
1class Job <ActiveRecord::Base
2 belongs_to :client, :polymorphic=>:true
3 attr_accessible :client_attributes
4 accepts_nested_attributes_for :client
5
6 def attributes=(attributes = {})
7 self.client_type = attributes[:client_type]
8 super
9 end
10
11 def client_attributes=(attributes)
12 some_client = self.client_type.constantize.find_or_initilize_by_id(self.client_id)
13 some_client.attributes = attributes
14 self.client = some_client
15 end
16end
so i tried this solution
1class Job <ActiveRecord::Base
2 belongs_to :client, :polymorphic=>:true, :autosave=>true
3 accepts_nested_attributes_for :client
4
5 def attributes=(attributes = {})
6 self.client_type = attributes[:client_type]
7 super
8 end
9
10 def client_attributes=(attributes)
11 self.client = eval(type).find_or_initialize_by_id(attributes.delete(:client_id)) if client_type.valid?
12 end
13end
and i had to modify it to suite my code like so
1class Vehicle < ActiveRecord::Base
2 MOVABLES = [:car, :bus, :plane]
3 belongs_to :movable, polymorphic: true, dependent: :destroy, required: true, autosave: true
4 accepts_nested_attributes_for :movable
5
6 def attributes=(attributes = {})
7 self.movable_type = attributes[:movable_type]
8 super
9 end
10
11 def movable_attributes=(attributes)
12 self.movable = eval(type).find_or_initialize_by_id(attributes.delete(:movable_id)) if movable_type.valid?
13 end
14end
but yeah, that didn’t work either so i got the idea from the previous code movable_attributes=
is the method that is invoked
when assigning the the mobable_attributes hash, so i have to override it to create/update the current child as follows
1class Vehicle < ActiveRecord::Base
2 MOVABLES = [:car, :bus, :plane]
3 belongs_to :movable, polymorphic: true, dependent: :destroy, required: true, autosave: true
4 accepts_nested_attributes_for :movable
5
6 def movable_attributes=(attributes)
7 if MOVABLES.include?(movable_type.underscore.to_sym)
8 self.movable ||= self.movable_type.constantize.new
9 self.movable.assign_attributes(attributes)
10 end
11 end
12end
that will create a movable object if it doesn’t exist, and it’ll update it in case it
already exists.
i wish rails would behave like this by default, and i have no idea why it doesn’t do that as it is
a trivial solution.