This 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
What I want to do now is something like this: 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 is not working because of the polymorphic assignment. How do I approach this problem?
EDIT: The Error is:
undefined method `base_class' for String:Class
with params being:
"speaker"=>{"speakable"=>"1063885469", "session_id"=>"1007692731"}
The value passed to speakable is the id of the Speaker/Company. Yes, this is the value I specified the collection_select
to return, but how can I manage to supply both values (speakable_id
and speakable_type
) ?
You can use a hidden_field
to save the speakable_type
. This way you'll have to change the value of the hidden_field
every time the selected option changes. I'm not sure this is the best approach, but works...
Hope it helps you.
Edit
You'll have to define the :onchange
option in your select.
<%= sf.collection_select :speakable, Company.all + Person.all, :id,
:full_name, {}, {:onchange => "setSpeakableType()"} %>
The hidden_field
starts with nil
value.
<%= sf.hidden_field :speakable_type, :value => nil %>
And your setSpeakableType()
function will have to set the proper speakable_type
.
Two ways I've solved this.
The first is to munge your collection in the form, so that you can figure out which type the id is to begin 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 to use javascript to change the hidden fields speakable_type and speakable_id which are hidden in the page. I ended up using an autocompleter to fill out this field, so invoking javascript when a selection was made was easy enough. The second way makes the controller a lot cleaner.
精彩评论