aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAgis- <corestudiosinc@gmail.com>2015-08-24 11:27:06 +0300
committerAgis- <corestudiosinc@gmail.com>2015-08-24 11:49:43 +0300
commit19b168e611fb8fb547981c4535130c29856efd3a (patch)
tree14ad6450b7da0940102d7a384b66fb55e4404935
parentef8d09d932e36b0614905ea5bc3fb6af318b6ce2 (diff)
downloadrails-19b168e611fb8fb547981c4535130c29856efd3a.tar.gz
rails-19b168e611fb8fb547981c4535130c29856efd3a.tar.bz2
rails-19b168e611fb8fb547981c4535130c29856efd3a.zip
Only nullify persisted has_one target associations
Since after 87d1aba3c `dependent: :destroy` callbacks on has_one assocations run *after* destroy, it is possible that a nullification is attempted on an already destroyed target: class Car < ActiveRecord::Base has_one :engine, dependent: :nullify end class Engine < ActiveRecord::Base belongs_to :car, dependent: :destroy end > car = Car.create! > engine = Engine.create!(car: car) > engine.destroy! # => ActiveRecord::ActiveRecordError: cannot update a > destroyed record In the above case, `engine.destroy!` deletes `engine` and *then* triggers the deletion of `car`, which in turn triggers a nullification of `engine.car_id`. However, `engine` is already destroyed at that point. Fixes #21223.
-rw-r--r--activerecord/CHANGELOG.md6
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb8
-rw-r--r--activerecord/test/models/developer.rb1
-rw-r--r--activerecord/test/models/ship.rb1
-rw-r--r--activerecord/test/schema/schema.rb1
6 files changed, 18 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 0841b2f679..d880470d97 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Only try to nullify has_one target association if the record is persisted.
+
+ Fixes #21223.
+
+ *Agis Anastasopoulos*
+
* Uniqueness validator raises descriptive error when running on a persisted
record without primary key.
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 5a92bc5e8a..1829453d73 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -65,7 +65,7 @@ module ActiveRecord
when :destroy
target.destroy
when :nullify
- target.update_columns(reflection.foreign_key => nil)
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
end
end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 5a8afaf4d2..d46e7ad235 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -107,6 +107,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nil Account.find(old_account_id).firm_id
end
+ def test_nullification_on_destroyed_association
+ developer = Developer.create!(name: "Someone")
+ ship = Ship.create!(name: "Planet Caravan", developer: developer)
+ ship.destroy
+ assert !ship.persisted?
+ assert !developer.persisted?
+ end
+
def test_natural_assignment_to_nil_after_destroy
firm = companies(:rails_core)
old_account_id = firm.account.id
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index d2a5a7fc49..8ac7a9df6a 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -50,6 +50,7 @@ class Developer < ActiveRecord::Base
has_many :firms, :through => :contracts, :source => :firm
has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") }
has_many :ratings, through: :comments
+ has_one :ship, dependent: :nullify
scope :jamises, -> { where(:name => 'Jamis') }
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 95172e4d3e..e333b964ab 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -3,6 +3,7 @@ class Ship < ActiveRecord::Base
belongs_to :pirate
belongs_to :update_only_pirate, :class_name => 'Pirate'
+ belongs_to :developer, dependent: :destroy
has_many :parts, :class_name => 'ShipPart'
has_many :treasures
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 6f34115534..994ece9244 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -673,6 +673,7 @@ ActiveRecord::Schema.define do
create_table :ships, force: true do |t|
t.string :name
t.integer :pirate_id
+ t.belongs_to :developer
t.integer :update_only_pirate_id
# Conventionally named column for counter_cache
t.integer :treasures_count, default: 0