diff options
author | Laerti <laertis.pappas@gmail.com> | 2019-01-15 15:03:20 +0100 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2019-01-15 23:03:20 +0900 |
commit | 41ffddbc8b7faec66a26bd48dfd36a9def6cc23f (patch) | |
tree | 578282157b3386a787237e9f8fa934010aa6233c /activerecord | |
parent | e4213e7c682f2b4bf19383ea1af1681c81c96ac5 (diff) | |
download | rails-41ffddbc8b7faec66a26bd48dfd36a9def6cc23f.tar.gz rails-41ffddbc8b7faec66a26bd48dfd36a9def6cc23f.tar.bz2 rails-41ffddbc8b7faec66a26bd48dfd36a9def6cc23f.zip |
Refs #28025 nullify *_type column on polymorphic associations on :nu… (#28078)
This PR addresses the issue described in #28025. On `dependent: :nullify` strategy only the foreign key of the relation is nullified. However on polymorphic associations the `*_type` column is not nullified leaving the record with a NULL `*_id` but the `*_type` column is present.
Diffstat (limited to 'activerecord')
9 files changed, 63 insertions, 4 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f1a5e58da6..e987a0e279 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Set polymorphic type column to NULL on `dependent: :nullify` strategy. + + On polymorphic associations both the foreign key and the foreign type columns will be set to NULL. + + *Laerti Papa* * Allow `ActionController::Params` as argument of `ActiveRecord::Base#exists?`. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fb1df00dc8..7bdbd8ce69 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1293,7 +1293,8 @@ module ActiveRecord # # * <tt>:destroy</tt> causes all the associated objects to also be destroyed. # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed). - # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed. + # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified + # on polymorphic associations. Callbacks are not executed. # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records. # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects. # @@ -1436,7 +1437,8 @@ module ActiveRecord # # * <tt>:destroy</tt> causes the associated object to also be destroyed # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute) - # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed. + # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified + # on polymorphic associations. Callbacks are not executed. # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object # diff --git a/activerecord/lib/active_record/associations/foreign_association.rb b/activerecord/lib/active_record/associations/foreign_association.rb index 40010cde03..59af6f54c3 100644 --- a/activerecord/lib/active_record/associations/foreign_association.rb +++ b/activerecord/lib/active_record/associations/foreign_association.rb @@ -9,5 +9,12 @@ module ActiveRecord::Associations false end end + + def nullified_owner_attributes + Hash.new.tap do |attrs| + attrs[reflection.foreign_key] = nil + attrs[reflection.type] = nil if reflection.type.present? + end + end end end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index f6fdbcde54..eb22db838c 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -92,7 +92,7 @@ module ActiveRecord if method == :delete_all scope.delete_all else - scope.update_all(reflection.foreign_key => nil) + scope.update_all(nullified_owner_attributes) end end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 390bfd8b08..99971286a3 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -33,7 +33,7 @@ module ActiveRecord target.destroy throw(:abort) unless target.destroyed? when :nullify - target.update_columns(reflection.foreign_key => nil) if target.persisted? + target.update_columns(nullified_owner_attributes) if target.persisted? end end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 5921193374..4b9b55f822 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1815,6 +1815,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal num_accounts, Account.count end + def test_depends_and_nullify_on_polymorphic_assoc + author = PersonWithPolymorphicDependentNullifyComments.create!(first_name: "Laertis") + comment = posts(:welcome).comments.first + comment.author = author + comment.save! + + assert_equal comment.author_id, author.id + assert_equal comment.author_type, author.class.name + + author.destroy + comment.reload + + assert_nil comment.author_id + assert_nil comment.author_type + end + def test_restrict_with_exception firm = RestrictedWithExceptionFirm.create!(name: "restrict") firm.companies.create(name: "child") diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index bf574f6637..3e5b5c1275 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -12,6 +12,9 @@ require "models/bulb" require "models/author" require "models/image" require "models/post" +require "models/drink_designer" +require "models/chef" +require "models/department" class HasOneAssociationsTest < ActiveRecord::TestCase self.use_transactional_tests = false unless supports_savepoints? @@ -114,6 +117,21 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_nil Account.find(old_account_id).firm_id end + def test_nullify_on_polymorphic_association + department = Department.create! + designer = DrinkDesignerWithPolymorphicDependentNullifyChef.create! + chef = department.chefs.create!(employable: designer) + + assert_equal chef.employable_id, designer.id + assert_equal chef.employable_type, designer.class.name + + designer.destroy! + chef.reload + + assert_nil chef.employable_id + assert_nil chef.employable_type + end + def test_nullification_on_destroyed_association developer = Developer.create!(name: "Someone") ship = Ship.create!(name: "Planet Caravan", developer: developer) diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb index eb6701b84e..8258408f35 100644 --- a/activerecord/test/models/drink_designer.rb +++ b/activerecord/test/models/drink_designer.rb @@ -4,5 +4,11 @@ class DrinkDesigner < ActiveRecord::Base has_one :chef, as: :employable end +class DrinkDesignerWithPolymorphicDependentNullifyChef < ActiveRecord::Base + self.table_name = "drink_designers" + + has_one :chef, as: :employable, dependent: :nullify +end + class MocktailDesigner < DrinkDesigner end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 5cba1e440e..c3d15a571a 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -62,6 +62,11 @@ class PersonWithDependentNullifyJobs < ActiveRecord::Base has_many :jobs, source: :job, through: :references, dependent: :nullify end +class PersonWithPolymorphicDependentNullifyComments < ActiveRecord::Base + self.table_name = "people" + has_many :comments, as: :author, dependent: :nullify +end + class LoosePerson < ActiveRecord::Base self.table_name = "people" self.abstract_class = true |