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.
This commit is contained in:
Laerti 2019-01-15 15:03:20 +01:00 committed by Ryuta Kamizono
parent e4213e7c68
commit 41ffddbc8b
10 changed files with 65 additions and 6 deletions

View File

@ -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?`.

View File

@ -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
#

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1257,7 +1257,7 @@ Controls what happens to the associated object when its owner is destroyed:
* `:destroy` causes the associated object to also be destroyed
* `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute)
* `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed.
* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed.
* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there is an associated record
* `:restrict_with_error` causes an error to be added to the owner if there is an associated object
@ -1658,7 +1658,7 @@ Controls what happens to the associated objects when their owner is destroyed:
* `:destroy` causes all the associated objects to also be destroyed
* `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute)
* `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed.
* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed.
* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there are any associated records
* `:restrict_with_error` causes an error to be added to the owner if there are any associated objects