How can I create a polymorphic model using collection_select?
These are my models:
class Speaker < ActiveRecord::Base
belongs_to :session, :foreign_key => :session_id, :class_name => :Session
belongs_to :speakable, :polymorphic => true
end
class Session < ActiveRecord::Base
has_many :speakers
accepts_nested_attributes_for :speakers
end
class Person < ActiveRecord::Base
has_many :speakers, :as => :speakable
end
class Company < ActiveRecord::Base
has_many :speakers, :as => :speakable
end
Now I want to do the following: app / views / sessions / edit.html.erb
<% f.fields_for :speakers do |sf| %>
<p>
<%= sf.label :speaker %><br />
<%= sf.collection_select :speakable, Company.all + Person.all, :id, :full_name %>
</p>
<% end %>
But it doesn't work because of the polymorphic assignment. How can I approach this problem?
EDIT: Error:
undefined method `base_class' for String:Class
with parameters:
"speaker"=>{"speakable"=>"1063885469", "session_id"=>"1007692731"}
The value passed in as the speaker is the Speaker / Company identifier. Yes, it's a value that I have to go back collection_select
, but I can manage two values ( speakable_id
and speakable_type
)?
a source to share
I solved it two ways.
The first is to create your collection on the form so you can define what type the identifier should start with. Something similar to this:
And then in your controller action:
def action
if params[:speaker][:speakable].begins_with?("Person:")
speak_type = 'Person'
speak_id = params[:speaker][:speakable].split(":")[1].to_i
elsif params[:speaker][:speakable].begins_with?("Company:")
speak_type = 'Company'
speak_id = params[:speaker][:speakable].split(":")[1].to_i
end
params[:speaker].delete(:speakable)
obj = Speaker.new(params[:speaker])
obj.speaker_type = speak_type
obj.speaker_id = speak_id
... rest of action ...
end
The second is using javascript to change the hidden speakable_type and sayable_id fields that are hidden on the page. I ended up using autocomplete to fill in this field, so the javascript reference for the selection was easy enough. The second way makes the controller a clean lot .
a source to share
You can use hidden_field
to save speakable_type
. Thus, you will have to change the value hidden_field
every time the selected parameter changes. I'm not sure if this is the best approach, but it works ...
Hope this helps you.
Edit
You need to define an option :onchange
in your choice.
<%= sf.collection_select :speakable, Company.all + Person.all, :id,
:full_name, {}, {:onchange => "setSpeakableType()"} %>
hidden_field
starts with a value nil
.
<%= sf.hidden_field :speakable_type, :value => nil %>
And your function setSpeakableType()
should set the correct one speakable_type
.
a source to share