- Generate migration for belongs_to association
- The generated migration might not work
- Use to_table of foreign_key option
- Afterwards
TL;DR: Provide the to_table option like this: foreign_key: {to_table: :<table_name>}.
Generate migration for belongs_to association
It might not be obvious how to write a Rails migration for a belongs_to association that’s joined to a table whose name doesn’t correspond to the association name. Read this post to see how to do that.
Imagine the following scenario:
- There is a
Usermodel in the system already. - We need to add a
Paymentmodel. -
Paymentshould belong to areceiver(business name), that’sUser(the model).
In the code, it looks like this:
class User < ApplicationRecord
end
class Payment < ApplicationRecord
belongs_to :receiver, class_name: 'User'
end
We generate this model using the built-in Rails generator:
rails g model payment receiver:references
It also generates this migration to create the corresponding table in the database:
class CreatePayments < ActiveRecord::Migration[6.0]
def change
create_table :payments do |t|
t.belongs_to :receiver, null: false, foreign_key: true
t.timestamps
end
end
end
The generated migration might not work
But running rake db:migrate produces the following error:
PG::UndefinedTable: ERROR: relation "receivers" does not exist
From the error message, it’s clear that the migration tries to add a foreign key for a nonexistent table named receivers. This name is derived from the belongs_to association name provided within the migration :receiver. The corresponding table name for it is receivers. This is where this name came from. And there is nothing wrong with that because in most cases we have a corresponding table name defined to a belongs_to association. But in our case, there is no receivers table. We want the users table used instead. Also, we want the column name to still correspond to the association name that’s receiver_id.
Use to_table of foreign_key option
Unfortunately, the official Rails documentation doesn’t explain how to write the migration for this scenario (later, I improved it in this PR). I checked Rails code and found a solution there. Just specify the option foreign_key: {to_table: :users} as below:
class CreatePayments < ActiveRecord::Migration[6.0]
def change
create_table :payments do |t|
t.belongs_to :receiver, null: false, foreign_key: {to_table: :users}
t.timestamps
end
end
end
Now the migration runs without failures and produces the desired result. It creates the payments table with the receiver_id column and the foreign key points to the joined users table.
Afterwards
To summarize, for a custom joined table or column name (that’s the same scenario) in a belongs_to migration, use foreign_key: {to_table: :users} like in the example above.
Based on this investigation, I created this PR to improve the documentation.
Thanks to everyone who supported it and made this merge happen so fast!
