mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #33954 from febeling/inconsistent-assignment-has-many-through-33942
Fix handling of duplicates for `replace` on has_many-through
This commit is contained in:
commit
023a840f5f
4 changed files with 43 additions and 3 deletions
|
@ -413,9 +413,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def replace_records(new_target, original_target)
|
||||
delete(target - new_target)
|
||||
delete(difference(target, new_target))
|
||||
|
||||
unless concat(new_target - target)
|
||||
unless concat(difference(new_target, target))
|
||||
@target = original_target
|
||||
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
||||
"new records could not be saved."
|
||||
|
@ -425,7 +425,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def replace_common_records_in_memory(new_target, original_target)
|
||||
common_records = new_target & original_target
|
||||
common_records = intersection(new_target, original_target)
|
||||
common_records.each do |record|
|
||||
skip_callbacks = true
|
||||
replace_on_target(record, @target.index(record), skip_callbacks)
|
||||
|
|
|
@ -130,6 +130,14 @@ module ActiveRecord
|
|||
end
|
||||
saved_successfully
|
||||
end
|
||||
|
||||
def difference(a, b)
|
||||
a - b
|
||||
end
|
||||
|
||||
def intersection(a, b)
|
||||
a & b
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -163,6 +163,28 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def difference(a, b)
|
||||
distribution = distribution(b)
|
||||
|
||||
a.reject { |record| mark_occurrence(distribution, record) }
|
||||
end
|
||||
|
||||
def intersection(a, b)
|
||||
distribution = distribution(b)
|
||||
|
||||
a.select { |record| mark_occurrence(distribution, record) }
|
||||
end
|
||||
|
||||
def mark_occurrence(distribution, record)
|
||||
distribution[record] > 0 && distribution[record] -= 1
|
||||
end
|
||||
|
||||
def distribution(array)
|
||||
array.each_with_object(Hash.new(0)) do |record, distribution|
|
||||
distribution[record] += 1
|
||||
end
|
||||
end
|
||||
|
||||
def through_records_for(record)
|
||||
attributes = construct_join_attributes(record)
|
||||
candidates = Array.wrap(through_association.target)
|
||||
|
|
|
@ -586,6 +586,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
|||
assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
|
||||
end
|
||||
|
||||
def test_replace_association_with_duplicates
|
||||
post = posts(:thinking)
|
||||
person = people(:david)
|
||||
|
||||
assert_difference "post.people.count", 2 do
|
||||
post.people = [person]
|
||||
post.people = [person, person]
|
||||
end
|
||||
end
|
||||
|
||||
def test_replace_order_is_preserved
|
||||
posts(:welcome).people.clear
|
||||
posts(:welcome).people = [people(:david), people(:michael)]
|
||||
|
|
Loading…
Reference in a new issue