This project is archived and is in readonly mode.

#2821 ✓hold
sarah (at ultrasaurus)

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

Reported by sarah (at ultrasaurus) | June 20th, 2009 @ 04:19 AM | in 2.x

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

Comments and changes to this ticket

  • sarah (at ultrasaurus)

    sarah (at ultrasaurus) June 20th, 2009 @ 04:46 AM

    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
    
  • Eloy Duran

    Eloy Duran June 20th, 2009 @ 12:19 PM

    • Assigned user set to “Eloy Duran”
  • sarah (at ultrasaurus)

    sarah (at ultrasaurus) June 20th, 2009 @ 06:29 PM

    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?

  • Eloy Duran

    Eloy Duran July 12th, 2009 @ 01:20 PM

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

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages