Skip to content

Commit b85c142

Browse files
committed
Set a source association when setting a belongs_to association
When a through association that is a belongs_to association is set, we don't set the source association, which might be confusing, because the source association is available after saving and reloading the record. For example: class Post < ActiveRecord::Base belongs_to :author has_many :comments end class Comment < ActiveRecord::Base belongs_to :post has_one :post_author, through: :post end class Author < ActiveRecord::Base has_many :posts end The post association in this case is a belongs_to association and also it's used as a through association. Before this commit the result of assigning the post to the comment would be the following: comment = Comment.new(text: "Just a comment") author = Author.new(name: "drogus") post = Post.new(title: "A title", author: author) comment.post = post comment.post_author #=> nil comment.save comment.reload comment.post_author #=> #<Author name: "drogus"> With a fix introduced in the commit, the post_author would be set before saving the record: comment = Comment.new(text: "Just a comment") author = Author.new(name: "drogus") post = Post.new(title: "A title", author: author) comment.post = post comment.post_author #=> #<Author name: "drogus"> comment.save comment.reload comment.post_author #=> #<Author name: "drogus">
1 parent 3333622 commit b85c142

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

activerecord/lib/active_record/associations/belongs_to_association.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ def replace(record)
7171
replace_keys(record)
7272

7373
self.target = record
74+
75+
through_reflection = self.reflection.active_record.reflections.values.find { |r| r.through_reflection && r.through_reflection.name == self.reflection.name }
76+
if through_reflection && !through_reflection.collection?
77+
through_target = record.send(through_reflection.name)
78+
if through_target
79+
source_target_name = through_reflection.source_reflection.name
80+
owner.send("#{source_target_name}=", through_target)
81+
end
82+
end
7483
end
7584

7685
def update_counters(by)

activerecord/test/cases/associations/has_one_through_associations_test.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,21 @@ def test_creating_association_sets_both_parent_ids_for_new
103103
assert_equal club.id, member.current_membership.club_id
104104
end
105105

106+
def test_assigning_a_belongs_to_through_record_also_assigns_the_source_record
107+
# setting ids is needed here because of `self.primary_key=` in models
108+
minivan = Minivan.new(name: "A minivan", minivan_id: 999)
109+
dashboard = Dashboard.new(name: "A dashboard", dashboard_id: 1000)
110+
speedometer = Speedometer.new(dashboard: dashboard, speedometer_id: 1001)
111+
112+
minivan.speedometer = speedometer
113+
114+
assert_equal dashboard, minivan.dashboard
115+
116+
minivan.save!
117+
minivan.reload
118+
119+
assert_equal dashboard, minivan.dashboard
120+
end
106121

107122
def test_assigning_a_has_one_through_record_also_assigns_the_source_record
108123
club = Club.new(name: "Da Club")

0 commit comments

Comments
 (0)