Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by InterstellarSatellite998

How Do I Configure Fabricator to Use a Custom Foreign Key for Rails STI Associations?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I have the following models in Rails 6.1.7:

RUBY
class AdminUser < ApplicationRecord belongs_to :organization, foreign_key: 'type_id' end class OrganizationUser < AdminUser end

The fabricators are defined in spec/fabricators/admin_user_fabricator.rb as follows:

RUBY
Fabricator(:admin_user) do email { Forgery(:internet).email_address } first_name { Forgery(:name).first_name } last_name { Forgery(:name).last_name } end Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do organization end

When I call the fabrication in my RSpec test:

RUBY
Fabricate.attributes_for(:organization_user)

I get the following output:

JSON
{"email":"wwright@buzzster.edu", "first_name":"Carolyn", "last_name":"Gutierrez", "organization_id":6919}

This causes an error:

ActiveModel::UnknownAttributeError: unknown attribute 'organization_id' for OrganizationUser.

The attribute "organization_id" shouldn’t be there. Instead, I expect to see "type" with the value "OrganizationUser" and a "type_id" attribute (e.g. 6939).

I suspect the issue is that Fabrication automatically generates an attribute based on the association name, ignoring the custom foreign key 'type_id'. I've tried several adjustments, but I’m not sure what’s wrong. How can I modify my fabricators to work correctly with this custom foreign key and STI setup?

1

Comments 0

Answers 3

Views 98

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Based on your description, the problem is that Fabrication is automatically creating an attribute key called "organization_id" from your association declaration:

RUBY
belongs_to :organization, foreign_key: 'type_id'

Fabrication isn’t aware of the custom foreign key (type_id) and so produces "organization_id". When Rails tries to assign organization_id to your OrganizationUser (which inherits from AdminUser), it raises an ActiveModel::UnknownAttributeError because there is no organization_id column.

To fix this, you have a couple of options:

1. Update Your Fabricator to Use the Correct Foreign Key

Instead of fabricating the association using the name “organization”, you can explicitly assign the organization using the correct attribute key. For example, if you have another fabricator for Organization (or the appropriate model), you can do:

RUBY
Fabricator(:admin_user) do email { Forgery(:internet).email_address } first_name { Forgery(:name).first_name } last_name { Forgery(:name).last_name } end Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do type_id { Fabricate(:organization).id } end

Here, instead of simply writing organization, you assign the organization’s id to type_id, which matches your association's foreign key.

2. Customize the Fabrication for Associations

If you want to continue using the association helper (i.e. organization), you need to let Fabrication know about the custom key. Fabrication doesn’t automatically look up the association’s foreign_key option. One workaround is to overwrite the attribute in your fabricator block after the association is set:

RUBY
Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do organization do Fabricate(:organization) end # Override the automatically generated organization_id attribute with type_id after_build do |user| user.type_id = user.organization.id if user.organization # Remove the incorrect attribute if needed (depending on how Fabrication handles it) user.attributes.delete('organization_id') end end

This way, once the organization is built and assigned to the user, you can manually copy its id into type_id and remove the incorrect organization_id attribute.

Additional Note on STI

Since your OrganizationUser inherits from AdminUser, Rails is using single table inheritance (STI), which means a “type” column is usually maintained automatically. Make sure your database is set up accordingly so that Rails sets the type column to "OrganizationUser" when you instantiate a record of that class.

Conclusion

The core of the issue is that when using a custom foreign key (type_id), Fabrication defaults to generating association_name + _id (i.e. organization_id). By updating your fabricators to assign the organization id to the correct attribute (type_id) or by overriding the generated attribute, you can resolve the error.

If you have further questions about Rails plugins, factories, or associations, feel free to ask!

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by SolarCommander191 1 month ago

0

There seems to be some confusion between your database tables structure, your models and the way Fabrication gem works.

The type field

It looks like you're working with Single Table Inheritance, so it makes sense to expect your database table to contain the type column. And you can verify that by running OrganizationUser.column_names or AdminUser.column_names. If STI was generated correctly, you should see the type column listed there.

This column will be automatically set whenever you instantiate an object for the child class.

RUBY
> OrganizationUser.new.type => "OrganizationUser"

And a similar result is expected if you build an OrganizationUser object with fabrication:

RUBY
> organization_user = Fabricate.build(:organization_user) > organization_user.type => "OrganizationUser"

However, when you call Fabricate.attributes_for, you're not instantiating a model object. attributes_for only loads what's present in the definitions for your fabrications. That's why you don't see the type field listed there. Your fabricators have no type field. If you want it to be returned, you must explicitly add type "OrganizationUser" to your organization_user fabricator.

The organization_id attribute and the type_id column

The organization_id attribute is returned when you call for Fabricate.attributes_for(:organization_user) because you specified the association relation in your fabricator. Similar to the topic above, note that attributes_for will return a list according to your fabricator definitions. If an association is passed, it will try to generate an ID based on it (regardless of which foreign key you defined in your model).

I don't see much about Organization in your question.
I'm assuming you also have an organizations table and an Organization model and that you added a type_id column to the admin_users table. You might want to go through those steps if any of these assumptions are incorrect. I'd suggest not using the type_id name for that FK unless you have a strong reason for that choice. If you could stick to the conventional FK organization_id, that can make your code easier to follow.

Fabricator suggestion

Assuming you're keeping type_id as the FK, you could have something like the following:

RUBY
Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do type "OrganizationUser" type_id { Fabricate(:organization).id } end

And then you call it on your specs without the need for extra params:

RUBY
attributes = Fabricate.attributes_for(:organization_user)

No comments yet.

Answer by AstralCosmonaut923 1 month ago

0

As Lidiane suggested, I fixed the issue with:

RUBY
let(:orguser_attrs) { Fabricate.attributes_for(:organization_user, type: 'OrganizationUser', type_id: Fabricate(:organization).id).except(:organization_id) }

Yeah, ugly as heck, I know. Bad designed models.

No comments yet.

Discussion

No comments yet.