Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix infinite recursion with foreign_key #47889

Merged
merged 1 commit into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions activerecord/lib/active_record/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -493,12 +493,12 @@ def join_table
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
end

def foreign_key
def foreign_key(infer_from_inverse_of: true)
@foreign_key ||= if options[:query_constraints]
# composite foreign keys support
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
else
-(options[:foreign_key]&.to_s || derive_foreign_key)
-(options[:foreign_key]&.to_s || derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of))
end
end

Expand Down Expand Up @@ -726,13 +726,13 @@ def derive_class_name
class_name.camelize
end

def derive_foreign_key
def derive_foreign_key(infer_from_inverse_of: true)
if belongs_to?
"#{name}_id"
elsif options[:as]
"#{options[:as]}_id"
elsif options[:inverse_of]
inverse_of.foreign_key
elsif options[:inverse_of] && infer_from_inverse_of
inverse_of.foreign_key(infer_from_inverse_of: false)
else
active_record.model_name.to_s.foreign_key
end
Expand Down
2 changes: 2 additions & 0 deletions activerecord/test/cases/reflection_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "cases/helper"
require "models/topic"
require "models/customer"
require "models/comment"
require "models/company"
require "models/company_in_module"
require "models/ship"
Expand Down Expand Up @@ -461,6 +462,7 @@ def test_never_validate_association_if_explicit
def test_foreign_key
assert_equal "author_id", Author.reflect_on_association(:posts).foreign_key.to_s
assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
assert_equal "comment_id", FirstPost.reflect_on_association(:comment_with_inverse).foreign_key.to_s
end

def test_foreign_key_is_inferred_from_model_name
Expand Down
2 changes: 2 additions & 0 deletions activerecord/test/models/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Comment < ActiveRecord::Base
belongs_to :first_post, foreign_key: :post_id
belongs_to :special_post_with_default_scope, foreign_key: :post_id

has_one :post_with_inverse, ->(comment) { where(id: comment.post_id) }, class_name: "FirstPost", inverse_of: :comment_with_inverse

has_many :children, class_name: "Comment", inverse_of: :parent
belongs_to :parent, class_name: "Comment", counter_cache: :children_count, inverse_of: :children

Expand Down
1 change: 1 addition & 0 deletions activerecord/test/models/post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class FirstPost < ActiveRecord::Base

has_many :comments, foreign_key: :post_id
has_one :comment, foreign_key: :post_id
has_one :comment_with_inverse, class_name: "Comment", inverse_of: :post_with_inverse
end

class PostWithDefaultSelect < ActiveRecord::Base
Expand Down