From 699a3725ad0a9a3f82c48fb7311cd4a72fa85fb8 Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Wed, 21 Jun 2023 17:01:44 -0500 Subject: [PATCH] Add support for unpersisted CPK has_one/has_many through associations Fixes loading of has_one and has_many associations with composite primary key though associations that have unpersisted owner records. --- .../associations/has_many_through_association.rb | 2 +- .../active_record/associations/through_association.rb | 4 +++- .../associations/has_many_through_associations_test.rb | 8 ++++++++ .../associations/has_one_through_associations_test.rb | 9 +++++++++ activerecord/test/models/cpk/book.rb | 5 +++++ 5 files changed, 26 insertions(+), 2 deletions(-) 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 e09d23f0b14fe..845e5e564266a 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -113,7 +113,7 @@ def remove_records(existing_records, records, method) end def target_reflection_has_associated_record? - !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?) + !(through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? { |foreign_key_column| owner[foreign_key_column].blank? }) end def update_through_counter?(method) diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 707d0bc1677f5..ac0911a25f007 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -86,7 +86,9 @@ def stale_state end def foreign_key_present? - through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil? + through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? do |foreign_key_column| + !owner[foreign_key_column].nil? + end end def ensure_mutable diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 1c155394c693b..07e9f312f5129 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1631,6 +1631,14 @@ def test_tags_has_manu_posts_through_association_with_composite_query_constraint assert_equal(expected_blog_post_ids.sort, blog_post_ids.sort) end + def test_loading_cpk_association_with_unpersisted_owner + order = Cpk::Order.create!(shop_id: 1) + book = Cpk::BookWithOrderAgreements.new(number: 2, author_id: 1, order: order) + order_agreement = Cpk::OrderAgreement.create!(order: order) + + assert_equal([order_agreement], book.order_agreements.to_a) + end + private def make_model(name) Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } } diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index afe1ec81fd6a2..e4e344a8635e2 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -22,6 +22,7 @@ require "models/carrier" require "models/shop_account" require "models/customer_carrier" +require "models/cpk" class HasOneThroughAssociationsTest < ActiveRecord::TestCase fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, @@ -445,4 +446,12 @@ def test_has_one_through_do_not_cache_association_reader_if_the_though_method_ha ensure CustomerCarrier.current_customer = nil end + + def test_loading_cpk_association_with_unpersisted_owner + order = Cpk::Order.create!(shop_id: 1) + book = Cpk::BookWithOrderAgreements.new(number: 2, author_id: 1, order: order) + order_agreement = Cpk::OrderAgreement.create!(order: order) + + assert_equal(order_agreement, book.order_agreement) + end end diff --git a/activerecord/test/models/cpk/book.rb b/activerecord/test/models/cpk/book.rb index 28aced1221403..077977e57f04d 100644 --- a/activerecord/test/models/cpk/book.rb +++ b/activerecord/test/models/cpk/book.rb @@ -20,4 +20,9 @@ class BrokenBook < Book class NullifiedBook < Book has_one :chapter, query_constraints: [:author_id, :book_number], dependent: :nullify end + + class BookWithOrderAgreements < Book + has_many :order_agreements, through: :order + has_one :order_agreement, through: :order, source: :order_agreements + end end