Skip to content

Commit ba19dbc

Browse files
authored
Merge pull request rails#47721 from Shopify/fix-nullifying-has-many-through-association
Fix nullification of has_many :through association with `query_constraints
2 parents 4b183e4 + 300c853 commit ba19dbc

File tree

3 files changed

+20
-2
lines changed

3 files changed

+20
-2
lines changed

activerecord/lib/active_record/associations/through_association.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def construct_join_attributes(*records)
5959

6060
association_primary_key = source_reflection.association_primary_key(reflection.klass)
6161

62-
if association_primary_key == reflection.klass.primary_key && !options[:source_type]
62+
if Array(association_primary_key) == reflection.klass.composite_query_constraints_list && !options[:source_type]
6363
join_attributes = { source_reflection.name => records }
6464
else
6565
join_attributes = {

activerecord/lib/active_record/persistence.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ def query_constraints_list # :nodoc:
499499
end
500500
end
501501

502+
# Returns an array of column names to be used in queries. The source of column
503+
# names is derived from +query_constraints_list+ or +primary_key+. This method
504+
# is for internal use when the primary key is to be treated as an array.
505+
def composite_query_constraints_list # :nodoc:
506+
@composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
507+
end
508+
502509
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
503510
# therefore all callbacks and filters are fired off before the object is deleted. This method is
504511
# less efficient than #delete but allows cleanup methods and other actions to be run.

activerecord/test/cases/associations_test.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
class AssociationsTest < ActiveRecord::TestCase
4343
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
4444
:computers, :people, :readers, :authors, :author_addresses, :author_favorites,
45-
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments
45+
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments, :sharded_tags, :sharded_blog_posts_tags
4646

4747
def test_eager_loading_should_not_change_count_of_children
4848
liquid = Liquid.create(name: "salty")
@@ -301,6 +301,17 @@ def test_append_composite_has_many_through_association_with_autosave
301301
assert_includes(blog_post.reload.tags, tag)
302302
assert_predicate Sharded::BlogPostTag.where(blog_post_id: blog_post.id, blog_id: blog_post.blog_id, tag_id: tag.id), :exists?
303303
end
304+
305+
def test_nullify_composite_has_many_through_association
306+
blog_post = sharded_blog_posts(:great_post_blog_one)
307+
assert_not_empty(blog_post.tags)
308+
309+
blog_post.tags = []
310+
311+
assert_empty(blog_post.tags)
312+
assert_empty(blog_post.reload.tags)
313+
assert_not_predicate Sharded::BlogPostTag.where(blog_post_id: blog_post.id, blog_id: blog_post.blog_id), :exists?
314+
end
304315
end
305316

306317
class AssociationProxyTest < ActiveRecord::TestCase

0 commit comments

Comments
 (0)