Take the 2-minute tour ×

This are my models:

class Speaker < ActiveRecord::Base
  belongs_to :session, :foreign_key => :session_id, :class_name => :Session
  belongs_to :speakable, :polymorphic => true

class Session < ActiveRecord::Base
  has_many :speakers
  accepts_nested_attributes_for :speakers

class Person < ActiveRecord::Base
  has_many :speakers, :as => :speakable

class Company < ActiveRecord::Base
  has_many :speakers, :as => :speakable

What I want to do now is something like this: app/views/sessions/edit.html.erb

  <% f.fields_for :speakers do |sf| %>
      <%= sf.label :speaker %><br />
      <%= sf.collection_select :speakable, Company.all + Person.all, :id, :full_name %>  
  <% 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) ?

share|improve this question
What error have you ? –  shingara May 11 '10 at 16:44

2 Answers 2

up vote 0 down vote accepted

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:

<% f.fields_for :speakers do |sf| %>

<%= sf.label :speaker %>
<% speakable_collection = Person.all.map {|x| [x.name, "Person:#{x.id}"]} + Company.all.map {|x| [x.name, "Company:#{x.id}"]} %> <%= sf.collection_select :speakable, speakable_collection, :id, :full_name %>

<% end %>

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'
  obj = Speaker.new(params[:speaker])
  obj.speaker_type = speak_type
  obj.speaker_id = speak_id
  ... rest of action ... 

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.

share|improve this answer
Thank you very much, this should work, even though it is very hackish :-) –  muxe May 12 '10 at 10:18

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.


You'll have to define the :onchange option in your select.

   :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.

share|improve this answer
Thank you for your answer. How do I set the value of the hidden field, since the speakable still has to be selected? –  muxe May 11 '10 at 17:47
Edited my answer :] –  j.. May 11 '10 at 18:20
I am not very good at JS, so I will check out the suggestion made by jamuraa, but I think your approach should work. –  muxe May 12 '10 at 10:21

Your Answer


