Any way around AssociationTypeMismatch?

I want to have popup menus and check boxes in my forms to let users
select associated objects. In the form I’m working on, the object
“belongs_to” another type of object which is selected from a popup menu.
The id of the chosen object(s) for association is passed back in the
parameter hash, but when rails creates a new object from this parameter
hash, I get a AssociationTypeMismatch. This error seems to be
complaining that rails expected an actual association object but it
instead got a string with the object’s id. I’m not sure why rails would
be expecting the actual associated object to be in the param hash
arriving from the form, but I’m still relatively new and am probably
doing something wrong.

Is there a way to instruct rails to expect an id in the param hash of a
form rather than the actual associated object? I think I could make
this work by overriding the ActiveRecord::Base update_attributes or
attributes= methods, but I’m betting this problem has been worked out
already since this is such a generic issue.

Additional Info:

I have a class owner which has_many :item, and a class item which
belongs_to :owner

On the form, I have

<%= select ‘item’, ‘owner’, Owner.find(:all).map{|c| [c.name,c.id]} %>

OK, I’ve stumbled upon a solution to the belongs_to case mentioned
above. The answer: use owner_id instead of owner in the form.

I still can’t seem to find the canonical rails way of processing
has_and_belongs_to_many associations as represented by a group of check
boxes yet. Is there a “built in” way to do this easily in rails?

For HABTM, something like the following should work:

<%= check_box_tag “relationships”, “some_id”, “checked_or_not” %>
<%= check_box_tag “relationships”, “some_other_id”, “checked_or_not” %>
<%= check_box_tag “relationships”, “some_other_other_id”,
“checked_or_not” %>

def update

@my_object.habtm_relationship_ids = params[:relationships]

end

That should do it. You’ll need to come up with some logic to
check/uncheck boxes when the form loads up.

How this works is that the multiple checkboxs all named the same
passes an array of id’s for your other relationship. Then setting the
ids field on the object does all the associating for the
relationships.

Probably a bit confusing, but I hope this helps.
-Nick

Thanks for responding. Using the relationship_ids=array avenue, I’m
trying to make this more DRY by adding it into ActiveRecord::Base so
associations will be handled automatically like other attributes during
calls to “new” or “update_attributes”. The lines starting with * are
the lines I’ve added. It works great on updates of existing records,
but for reasons still unclear to me it captures some but not all check
boxes when creating a new object. Am I barking up the wrong tree here?

class ActiveRecord::Base

def attributes=(attributes)
return if attributes.nil?

  • self.class.reflect_on_all_associations.each do |a|
  •   if(attributes[a.name])
    

self.send(Inflector.singularize(a.name.to_s)+"_ids=",attributes[a.name.to_s].map
{|x| x.to_i})

  •      attributes.delete(a.name.to_s)
    
  •   end
    
  • end

    attributes.stringify_keys!
    multi_parameter_attributes = []
    remove_attributes_protected_from_mass_assignment(attributes).each
    do |k, v|
    k.include?("(") ? multi_parameter_attributes << [ k, v ] :
    send(k + “=”, v)
    end
    assign_multiparameter_attributes(multi_parameter_attributes)
    end

end

What other ‘automatic’ feautres are looking for? Setting the array ids
will take care of everything for you. (ie deleting records that are no
long associated and the like). You shouldn’t need to do anything else.

Setup some test cases and take a look for yourself.

-Nick

Automatic in the sense that you can just say

@newobj=TheClass.new(param[:theclass])

or

@obj.update_attributes(param[:theclass])

without having to explicitly hand code for all the associations that the
object has.

So, in short:
@obj.update_attributes(param[:theclass])

instead of

@obj.update_attributes(param[:theclass])
@obj.attribute1_ids=param[:theclass][attribute1]
@obj.attribute2_ids=param[:theclass][attribute2]
@obj.attribute3_ids=param[:theclass][attribute3]
@obj.attribute4_ids=param[:theclass][attribute4]
@obj.attribute5_ids=param[:theclass][attribute5]
etc…

Just like the “new” or “update_attributes” function allows us to avoid
typing things such as:
@obj.name=param[:theclass][:name]
@obj.address=param[:theclass][:address]
@obj.phone=param[:theclass][:phone]
etc…

That lets me be up and running with existing scaffolding code (rails,
ajax_scaffold) without having to hand tweak each individual controller
new/update function to handle associations. I will just have to add the
check boxes and popup to the views code to handle associations.
Ideally, the scaffolding code would do that for me too so I can be even
more lazy than I already am.