diff options
Diffstat (limited to 'activerecord/lib')
3 files changed, 35 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 398936b3d8..b90838a52b 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1606,12 +1606,11 @@ module ActiveRecord send(reflection.name).each do |o| # No point in executing the counter update since we're going to destroy the parent anyway counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym - if(o.respond_to? counter_method) then + if o.respond_to?(counter_method) class << o self end.send(:define_method, counter_method, Proc.new {}) end - o.destroy end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 3174ea6373..c98ac79dc0 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -43,19 +43,18 @@ module ActiveRecord end end - # TODO - add dependent option support - def delete_records(records, method = @reflection.options[:dependent]) - through_association = @owner.send(@reflection.through_reflection.name) + def deletion_scope(records) + @owner.send(@reflection.through_reflection.name).where(construct_join_attributes(*records)) + end + def delete_records(records, method = @reflection.options[:dependent]) case method when :destroy - records.each do |record| - through_association.where(construct_join_attributes(record)).destroy_all - end + deletion_scope(records).destroy_all + when :nullify + deletion_scope(records).update_all(@reflection.source_reflection.foreign_key => nil) else - records.each do |record| - through_association.where(construct_join_attributes(record)).delete_all - end + deletion_scope(records).delete_all end end diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index c840a16160..4ae0669c96 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -74,21 +74,40 @@ module ActiveRecord right.create_on(right.create_and(conditions))) end - # Construct attributes for :through pointing to owner and associate. - def construct_join_attributes(associate) - # TODO: revisit this to allow it for deletion, supposing dependent option is supported - raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro) + # Construct attributes for :through pointing to owner and associate. This is used by the + # methods which create and delete records on the association. + # + # We only support indirectly modifying through associations which has a belongs_to source. + # This is the "has_many :tags, :through => :taggings" situation, where the join model + # typically has a belongs_to on both side. In other words, associations which could also + # be represented as has_and_belongs_to_many associations. + # + # We do not support creating/deleting records on the association where the source has + # some other type, because this opens up a whole can of worms, and in basically any + # situation it is more natural for the user to just create or modify their join records + # directly as required. + def construct_join_attributes(*records) + if @reflection.source_reflection.macro != :belongs_to + raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) + end join_attributes = { @reflection.source_reflection.foreign_key => - associate.send(@reflection.source_reflection.association_primary_key) + records.map { |record| + record.send(@reflection.source_reflection.association_primary_key) + } } if @reflection.options[:source_type] - join_attributes.merge!(@reflection.source_reflection.foreign_type => associate.class.base_class.name) + join_attributes[@reflection.source_reflection.foreign_type] = + records.map { |record| record.class.base_class.name } end - join_attributes + if records.count == 1 + Hash[join_attributes.map { |k, v| [k, v.first] }] + else + join_attributes + end end # The reason that we are operating directly on the scope here (rather than passing |