Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

accepts_nested_attributes_for on has_many does not update or delete if you also set validates_associated

#5115

Tested to be a problem in Rails 2.3.8 and working in Rails 2.3.5

If you have a has_many association with validation and then try to update that association with update_attributes on a newly loaded parent object, the validation will overload the objects updated in memory before the save.
Example:

 class Address < ActiveRecord::Base
  belongs_to :contact
  validates_inclusion_of :is_current, :in => [true, false], :allow_nil => true  #any validation will do 
 end


class Contact < ActiveRecord::Base has_many :addresses, :dependent => :destroy validates_associated :addresses
accepts_nested_attributes_for :addresses, :allow_destroy => true end


#now create a new contact with an address company = Contact.create :name => "test" newaddress = company.addresses.create :city => "Austin", :is_current => true #now reload the company from the db company = Contact.find company.id #now update attributes company.update_attributes(:addresses_attributes => [{:id => newaddress.id, :city => "Dallas", :is_current => true }] #now check the address city company.addresses.first.city #Will print out Austin
This is because the code in Rails 2.3.8 does not load the addresses association for the nested attributes update. Instead it tries to be clever and it loads only the objects for which it has IDs. This saves time, but when validation runs in sees that company.addresses.loaded? is false and it reloads the association, and wipes out the changes.
In activerecord/lib/active_record/nested_attributes.rb the code:
 existing_records = if association.loaded?

     association.to_a
   else
     attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
     attribute_ids.present? ? association.all(:conditions =&gt; {association.primary_key =&gt; attribute_ids}) : []
   end</code>



Seems to be the problem. If I just change it to
 existing_records = association.to_a
I have no problems.
In Rails 2.3.5 when it loops over the attributes array it uses this code to find the object
 existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s }
This will load the association the first time the detect is run, and avoid the problem with validation.

Reported by swalterd · July 15th, 2010 @ 12:49 AM

State: invalid
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. Neeraj Singh
    Neeraj Singh
    • Importance changed from to Low

    nested_attributes is totally broken in 2.3.8. Please try with 2-3-stable branch and let us know if the problem persists.

    July 15th, 2010 @ 03:42 AM

  2. swalterd
    swalterd

    It seems to work in Rails 2-3-stable but I will do more testing tomorrow.

    July 15th, 2010 @ 05:31 AM

  3. swalterd
    swalterd

    After more testing it appears that 2-3-stable checkout fixes the problem I was having.

    July 15th, 2010 @ 04:24 PM

  4. Neeraj Singh
    Neeraj Singh
    • State changed from new to invalid

    July 15th, 2010 @ 04:44 PM