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.

associations do not autosave when a child is modified by creating a "grandchild" thru nested attributes

#2821

accepts_nested_attributes_for is fabulous, but we've run into an issue where an object in our hierarchy doesn't save when we think it should.

Here's a test case that fails when added to: autosave_association_test.rb in the rails 2-3-stable branch

Pirate (new object)
  |
  |-> Ship (existing object identified by id)
       |
       |-> ShipPart (new object)

The ShipPart fails to save when saving Pirate. Both Pirate and Ship use accepts_nested_attributes to permit creation of the next lower child via attribute assignments.

Our speculation of the source of this bug is that autosave does not cascade past objects are are not new_record? or whose attributes are not changed?

class TestDeepAutosaveAssociation < ActiveRecord::TestCase
    self.use_transactional_fixtures = false

    def setup
        Ship.accepts_nested_attributes_for :parts

        @ship = Ship.create!(:name => "Bounty")
        assert_not_nil @ship
    end

    def test_that_new_grandchild_will_save_when_child_exists
      assert_difference "Pirate.count", 1 do
        assert_difference "ShipPart.count", 1 do
            assert_no_difference "Ship.count"  do
            @pirate = Pirate.create!(:catchphrase => "Arhh!", 
                            :ship_attributes => { :id => @ship.id,
                                                :parts_attributes => [ {:name => "plank"} ] })
            end
        end
      end

    end


end

Reported by sarah (at ultrasaurus) · June 20th, 2009 @ 04:19 AM

State: hold
Milestone: 2.x
Assigned to: Eloy Duran Eloy Duran
Importance: none

Activity

  1. sarah (at ultrasaurus)
    sarah (at ultrasaurus)

    We've discovered that if this is done without nested attributes it works fine. The following test passes:

    class TestDeepAutosaveAssociationRevised < ActiveRecord::TestCase
        self.use_transactional_fixtures = false
    
        def setup
            Ship.accepts_nested_attributes_for :parts
    
            @ship = Ship.create!(:name => "Bounty")
            assert_not_nil @ship
        end
    
        def test_that_new_grandchild_will_save_when_child_exists
          assert_difference "Pirate.count", 1 do
            assert_difference "ShipPart.count", 1 do
                assert_no_difference "Ship.count"  do
                    @pirate = Pirate.new(:catchphrase => "Arhh!")
                    @pirate.ship = @ship
                    @ship.parts.build(:name => "plank")
                    @pirate.save!
                end
            end
          end
    
        end
    
    
    end
    

    June 20th, 2009 @ 04:46 AM

  2. Eloy Duran
    Eloy Duran
    • Assigned user set to Eloy Duran

    June 20th, 2009 @ 12:19 PM

  3. sarah (at ultrasaurus)
    sarah (at ultrasaurus)

    Eloy -- thanks for taking this on. Now that I've looked at the code a little, I'm quite curious about this (aside from the practical need for a fix). We looked into this last night but couldn't really understand the autosave mechanism. In the second test which passes, how does Rails know that it needs to save the ship when @ship.changed? is false and only its association "parts" has changed?

    June 20th, 2009 @ 06:29 PM

  4. Eloy Duran
    Eloy Duran
    • State changed from new to hold

    Yes you are quite right, this is the problem. I haven't worked out yet how I'm going to fix this. But after breaking my head over it for a few hours I came to the conclusion that I don't have the right idea atm. I'll be on vacation for the next 3 weeks, so I hope that will clear my thoughts and I can come up with the right one when I'm back.

    July 12th, 2009 @ 01:20 PM