Fix #8856 Ensure has_one association=(associate) triggers save.

activerecord/lib/active_record/associations.rb states:

    # [association=(associate)]
    #   Assigns the associate object, extracts the primary key, sets it as the foreign key,
    #   and saves the associate object.

Since commit 42dd5d9f29 to fix #7191, this
is no longer the case if the associate has changed, but is the same
object. For example:

    # Pirate has_one :ship
    pirate = Pirate.create!(catchphrase: "A Pirate")
    ship = pirate.build_ship(name: 'old name')
    ship.save!

    ship.name = 'new name'
    pirate.ship = ship

That last line should trigger a save. Although we are not changing the
association, the associate (ship) has changed.
This commit is contained in:
Chris Thompson 2013-03-07 14:43:00 -07:00 committed by Aaron Patterson
parent e8e2f010af
commit ebd7cc6f45
3 changed files with 24 additions and 3 deletions

View File

@ -1,3 +1,9 @@
* Trigger a save on `has_one association=(associate)` when the associate contents have changed.
Fix #8856.
*Chris Thompson*
* Abort a rake task when missing db/structure.sql like `db:schema:load` task.
*kennyj*

View File

@ -25,9 +25,8 @@ module ActiveRecord
raise_on_type_mismatch!(record) if record
load_target
# If target and record are nil, or target is equal to record,
# we don't need to have transaction.
if (target || record) && target != record
return self.target if !(target || record)
if (target != record) || record.changed?
transaction_if(save) do
remove_target!(options[:dependent]) if target && !target.destroyed?

View File

@ -522,4 +522,20 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
account = Account.find(2)
assert_queries { company.account = account }
end
def test_has_one_assignment_triggers_save_on_change
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
ship = pirate.build_ship(name: 'old name')
ship.save!
ship.name = 'new name'
assert ship.changed?
assert_queries(2) do
# One query for updating name and second query for updating pirate_id
pirate.ship = ship
end
assert_equal 'new name', pirate.ship.reload.name
end
end