Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Respect association overrides over trait-defined foreign keys (#1767)
* Ensure association overrides take precedence over trait-defined foreign keys

  In 6.5.5, changes related to PR #1709 introduced an unintended behavior
  where a trait-defined `*_id` could take precedence over an explicit
  association override. This could lead to inconsistency between the
  association and its foreign key.

* Update `AttributeAssigner#aliased_attribute?` to prioritize associations

  When an association override is provided (e.g., `user: user_instance`),
  the corresponding trait-defined foreign key is ignored, keeping the object
  graph consistent with Rails/ActiveRecord expectations. The fix preserves
  the intent of PR #1709 regarding attribute/attribute_id conflicts.

* Add regression specs

  - Association override wins over trait-defined foreign key
  - Trait-defined foreign key applies when no association override is present
  - Multiple foreign-key traits remain supported

Example:

  Before (6.5.5):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => 999

  After (this change; consistent with 6.5.4):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => user.id

Related: #1709, #1767
  • Loading branch information
JinOketani committed Sep 12, 2025
commit 27953c46f8196edd24144bbf1ce55b6cda6022cd
4 changes: 4 additions & 0 deletions lib/factory_bot/attribute_assigner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ def aliased_attribute_names_to_ignore
# in the same factory.
#
def aliased_attribute?(attribute, override)
if association_names.include?(override) && attribute.alias_for?(override) && attribute.name != override
return true
end

return false if attribute_names.include?(override)

attribute.alias_for?(override)
Expand Down
54 changes: 54 additions & 0 deletions spec/acceptance/attribute_aliases_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,59 @@
expect(user.response).to eq "new response"
expect(user.response_id).to eq 13.75
end

context "when association overrides trait foreign key" do
before do
define_model("User", name: :string)
define_model("Post", user_id: :integer, title: :string) do
belongs_to :user, optional: true
end

FactoryBot.define do
factory :user do
name { "Test User" }
end

factory :post do
association :user
title { "Test Post" }

trait :with_system_user_id do
user_id { 999 }
end

trait :with_user_id_100 do
user_id { 100 }
end

trait :with_user_id_200 do
user_id { 200 }
end
end
end
end

it "prefers association override over trait foreign key" do
user = FactoryBot.create(:user)
post = FactoryBot.build(:post, :with_system_user_id, user: user)

expect(post.user_id).to eq(user.id)
expect(post.user).to eq(user)
end

it "uses trait foreign key when no association override is provided" do
post = FactoryBot.build(:post, :with_system_user_id)

expect(post.user_id).to eq(999)
end

it "handles multiple traits with foreign keys correctly" do
user = FactoryBot.create(:user)
post = FactoryBot.build(:post, :with_user_id_100, :with_user_id_200, user: user)

expect(post.user_id).to eq(user.id)
expect(post.user).to eq(user)
end
end
end
end