Add :dependent = to has_one and has_many [#3075 state:resolved]
This commit is contained in:
parent
76f024ac8d
commit
f1e5a9ff98
@ -86,6 +86,15 @@ def initialize(reflection)
|
||||
end
|
||||
end
|
||||
|
||||
# This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications
|
||||
# (has_many, has_one) when there is at least 1 child assosociated instance.
|
||||
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
||||
class DeleteRestrictionError < ActiveRecordError #:nodoc:
|
||||
def initialize(reflection)
|
||||
super("Cannot delete record because of dependent #{reflection.name}")
|
||||
end
|
||||
end
|
||||
|
||||
# See ActiveRecord::Associations::ClassMethods for documentation.
|
||||
module Associations # :nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
@ -831,6 +840,8 @@ module ClassMethods
|
||||
# objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
|
||||
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
|
||||
# the <tt>:through</tt> option.
|
||||
# the <tt>:through</tt> option. If set to <tt>:restrict</tt>
|
||||
# this object cannot be deleted if it has any associated object.
|
||||
# [:finder_sql]
|
||||
# Specify a complete SQL statement to fetch the association. This is a good way to go for complex
|
||||
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
|
||||
@ -1469,9 +1480,15 @@ def add_touch_callbacks(reflection, touch_attribute)
|
||||
|
||||
# Creates before_destroy callback methods that nullify, delete or destroy
|
||||
# has_many associated objects, according to the defined :dependent rule.
|
||||
# If the association is marked as :dependent => :restrict, create a callback
|
||||
# that prevents deleting entirely.
|
||||
#
|
||||
# See HasManyAssociation#delete_records. Dependent associations
|
||||
# delete children, otherwise foreign key is set to NULL.
|
||||
# See HasManyAssociation#delete_records. Dependent associations
|
||||
# delete children if the option is set to :destroy or :delete_all, set the
|
||||
# foreign key to NULL if the option is set to :nullify, and do not touch the
|
||||
# child records if the option is set to :restrict.
|
||||
#
|
||||
# The +extra_conditions+ parameter, which is not used within the main
|
||||
# Active Record codebase, is meant to allow plugins to define extra
|
||||
@ -1531,14 +1548,24 @@ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
|
||||
%@#{dependent_conditions}@)
|
||||
end
|
||||
CALLBACK
|
||||
when :restrict
|
||||
method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
unless send(reflection.name).empty?
|
||||
raise DeleteRestrictionError.new(reflection)
|
||||
end
|
||||
end
|
||||
before_destroy method_name
|
||||
else
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates before_destroy callback methods that nullify, delete or destroy
|
||||
# has_one associated objects, according to the defined :dependent rule.
|
||||
# If the association is marked as :dependent => :restrict, create a callback
|
||||
# that prevents deleting entirely.
|
||||
def configure_dependency_for_has_one(reflection)
|
||||
if reflection.options.include?(:dependent)
|
||||
name = reflection.options[:dependent]
|
||||
@ -1559,8 +1586,16 @@ def #{method_name}
|
||||
association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association
|
||||
end
|
||||
eoruby
|
||||
when :restrict
|
||||
method_name = "has_one_dependent_restrict_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
unless send(reflection.name).nil?
|
||||
raise DeleteRestrictionError.new(reflection)
|
||||
end
|
||||
end
|
||||
before_destroy method_name
|
||||
else
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
|
||||
end
|
||||
|
||||
before_destroy method_name
|
||||
|
@ -439,9 +439,15 @@ def test_dependent_delete_and_destroy_with_belongs_to
|
||||
assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids
|
||||
end
|
||||
|
||||
def test_invalid_belongs_to_dependent_option_raises_exception
|
||||
def test_invalid_belongs_to_dependent_option_nullify_raises_exception
|
||||
assert_raise ArgumentError do
|
||||
Author.belongs_to :special_author_address, :dependent => :nullify
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_belongs_to_dependent_option_restrict_raises_exception
|
||||
assert_raise ArgumentError do
|
||||
Author.belongs_to :special_author_address, :dependent => :restrict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -836,6 +836,14 @@ def test_depends_and_nullify
|
||||
assert_equal num_accounts, Account.count
|
||||
end
|
||||
|
||||
def test_restrict
|
||||
firm = RestrictedFirm.new(:name => 'restrict')
|
||||
firm.save!
|
||||
child_firm = firm.companies.create(:name => 'child')
|
||||
assert !firm.companies.empty?
|
||||
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
|
||||
end
|
||||
|
||||
def test_included_in_collection
|
||||
assert companies(:first_firm).clients.include?(Client.find(2))
|
||||
end
|
||||
|
@ -177,7 +177,15 @@ def test_dependence_with_nil_associate
|
||||
assert_nothing_raised { firm.destroy }
|
||||
end
|
||||
|
||||
def test_succesful_build_association
|
||||
def test_dependence_with_restrict
|
||||
firm = RestrictedFirm.new(:name => 'restrict')
|
||||
firm.save!
|
||||
account = firm.create_account(:credit_limit => 10)
|
||||
assert !firm.account.nil?
|
||||
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
|
||||
end
|
||||
|
||||
def test_successful_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
|
@ -95,6 +95,11 @@ class DependentFirm < Company
|
||||
has_many :companies, :foreign_key => 'client_of', :dependent => :nullify
|
||||
end
|
||||
|
||||
class RestrictedFirm < Company
|
||||
has_one :account, :foreign_key => "firm_id", :dependent => :restrict, :order => "id"
|
||||
has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :restrict
|
||||
end
|
||||
|
||||
class Client < Company
|
||||
belongs_to :firm, :foreign_key => "client_of"
|
||||
belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
|
||||
|
Loading…
Reference in New Issue
Block a user