Find an article

Start typing to search the archive.

← All articles

Rails migration for belongs_to association with a custom table name

Write a Rails migration for a belongs_to association with a different column name or a custom table name.

Updated

Rails migration for belongs_to association with a custom table name

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 User model in the system already.
  • We need to add a Payment model.
  • Payment should belong to a receiver (business name), that’s User (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!