diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index cbbf690f2032f..43479c346b991 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -189,7 +189,14 @@ def extensions def load_target @target = find_target(async: false) if (@stale_state && stale_target?) || find_target? if !@target && set_through_target_for_new_record? - @target = through_association.target.association(reflection.source_reflection_name).target + reflections = reflection.chain + reflections.pop + reflections.reverse! + + @target = reflections.reduce(through_association.target) do |middle_target, reflection| + break unless middle_target + middle_target.association(reflection.source_reflection_name).target + end end loaded! unless loaded? diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 5e85af7b49a24..6b1275af6b2d9 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -273,12 +273,18 @@ def load_target if find_target? @target = merge_target_lists(find_target, target) elsif target.empty? && set_through_target_for_new_record? - @target = if through_reflection.collection? - through_association.target.flat_map do |record| - record.association(reflection.source_reflection_name).target + reflections = reflection.chain + reflections.pop + reflections.reverse! + + @target = reflections.reduce(through_association.target) do |middle_target, reflection| + if middle_target.empty? + break [] + elsif reflection.collection? + middle_target.flat_map { |record| record.association(reflection.source_reflection_name).target } + else + middle_target.association(reflection.source_reflection_name).target end - else - through_association.target.association(reflection.source_reflection_name).target end end 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 825763f7ebec7..a45ca9df62952 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -41,6 +41,7 @@ require "models/zine" require "models/interest" require "models/human" +require "models/account" class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags, @@ -67,7 +68,24 @@ def test_setting_association_on_new_record_sets_through_records assert_predicate subscriber_2, :persisted? assert_predicate book, :new_record? book.subscriptions.each { |subscription| assert_predicate subscription, :new_record? } - assert_equal book.subscribers.sort, [subscriber_1, subscriber_2].sort + assert_equal [subscriber_1, subscriber_2].sort, book.subscribers.sort + end + + def test_setting_association_on_new_record_sets_nested_through_records + account_1 = Account.create!(firm_name: "account 1", credit_limit: 100) + subscriber_1 = Subscriber.create!(nick: "nick 1", account: account_1) + account_2 = Account.create!(firm_name: "account 2", credit_limit: 100) + subscriber_2 = Subscriber.create!(nick: "nick 2", account: account_2) + subscription_1 = Subscription.new(subscriber: subscriber_1) + subscription_2 = Subscription.new(subscriber: subscriber_2) + book = Book.new + book.subscriptions = [subscription_1, subscription_2] + + assert_predicate subscriber_1, :persisted? + assert_predicate subscriber_2, :persisted? + assert_predicate book, :new_record? + book.subscriptions.each { |subscription| assert_predicate subscription, :new_record? } + assert_equal [account_1, account_2].sort, book.subscriber_accounts.sort end def test_has_many_through_create_record 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 bfc9ebef572c7..4837c8d3cb96a 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -55,6 +55,20 @@ def test_setting_association_on_new_record_sets_through_record assert_equal club, member.club end + def test_setting_association_on_new_record_sets_nested_through_record + category = Category.create!(name: "General") + club = Club.create!(category: category) + membership = CurrentMembership.new(club: club) + member = Member.new + member.current_membership = membership + + assert_predicate category, :persisted? + assert_predicate club, :persisted? + assert_predicate member, :new_record? + assert_predicate member.current_membership, :new_record? + assert_equal club.category, member.club_category + end + def test_creating_association_creates_through_record new_member = Member.create(name: "Chris") new_member.club = Club.create(name: "LRUG") diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb index f6133c4ff6e64..cd4c5d35da393 100644 --- a/activerecord/test/models/book.rb +++ b/activerecord/test/models/book.rb @@ -9,6 +9,7 @@ class Book < ActiveRecord::Base has_many :subscriptions has_many :subscribers, through: :subscriptions + has_many :subscriber_accounts, through: :subscribers, source: :account has_one :essay diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb index bd298dae25d29..4aca21f6382ae 100644 --- a/activerecord/test/models/subscriber.rb +++ b/activerecord/test/models/subscriber.rb @@ -3,6 +3,8 @@ class Subscriber < ActiveRecord::Base self.primary_key = "nick" + belongs_to :account + has_many :subscriptions has_many :books, through: :subscriptions end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index f9762e83568e1..487ed2035c826 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -1173,6 +1173,7 @@ t.integer :books_count, null: false, default: 0 t.integer :update_count, null: false, default: 0 t.index :nick, unique: true + t.references :account end create_table :subscriptions, force: true do |t|