Rails Migration: add_reference to Table but Different Column Name For Foreign Key Than Rails Convention

Ruby on-RailsRails Migrations

Ruby on-Rails Problem Overview


I have the following two Models:

class Store < ActiveRecord::Base
    belongs_to :person
end

class Person < ActiveRecord::Base
    has_one :store
end

Here is the issue: I am trying to create a migration to create the foreign key within the people table. However, the column referring to the foreign key of Store is not named store_id as would be rails convention but is instead named foo_bar_store_id.

If I was following the rails convention I would do the migration like this:

class AddReferencesToPeople < ActiveRecord::Migration
  def change
    add_reference :people, :store, index: true
  end
end

However this will not work because the column name is not store_id but is foo_bar_store_id. So how do I specify that the foreign key name is just different, but still maintain index: true to maintain fast performance?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

in rails 5.x you can add a foreign key to a table with a different name like this:

class AddFooBarStoreToPeople < ActiveRecord::Migration[5.0]
  def change
    add_reference :people, :foo_bar_store, foreign_key: { to_table: :stores }
  end
end

Inside a create_table block

t.references :feature, foreign_key: {to_table: :product_features}

Solution 2 - Ruby on-Rails

In Rails 4.2, you can also set up the model or migration with a custom foreign key name. In your example, the migration would be:

class AddReferencesToPeople < ActiveRecord::Migration
  def change
    add_column :people, :foo_bar_store_id, :integer, index: true
    add_foreign_key :people, :stores, column: :foo_bar_store_id
  end
end

Here is an interesting blog post on this topic. Here is the semi-cryptic section in the Rails Guides. The blog post definitely helped me.

As for associations, explicitly state the foreign key or class name like this (I think your original associations were switched as the 'belongs_to' goes in the class with the foreign key):

class Store < ActiveRecord::Base
  has_one :person, foreign_key: :foo_bar_store_id
end

class Person < ActiveRecord::Base
  belongs_to :foo_bar_store, class_name: 'Store'
end

Note that the class_name item must be a string. The foreign_key item can be either a string or symbol. This essentially allows you to access the nifty ActiveRecord shortcuts with your semantically-named associations, like so:

person = Person.first
person.foo_bar_store
# returns the instance of store equal to person's foo_bar_store_id

See more about the association options in the documentation for belongs_to and has_one.

Solution 3 - Ruby on-Rails

To expand on schpet's answer, this works in a create_table Rails 5 migration directive like so:

create_table :chapter do |t|
  t.references :novel, foreign_key: {to_table: :books}
  t.timestamps
end

Solution 4 - Ruby on-Rails

EDIT: For those that see the tick and don't continue reading!

While this answer achieves the goal of having an unconventional foreign key column name, with indexing, it does not add a fk constraint to the database. See the other answers for more appropriate solutions using add_foreign_key and/or 'add_reference'.

Note: ALWAYS look at the other answers, the accepted one is not always the best!

Original answer:

In your AddReferencesToPeople migration you can manually add the field and index using:

add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id

And then let your model know the foreign key like so:

class Person < ActiveRecord::Base
  has_one :store, foreign_key: 'foo_bar_store_id'
end

Solution 5 - Ruby on-Rails

# Migration
change_table :people do |t|
  t.references :foo_bar_store, references: :store #-> foo_bar_store_id
end

# Model
# app/models/person.rb
class Person < ActiveRecord::Base
  has_one :foo_bar_store, class_name: "Store"
end

Solution 6 - Ruby on-Rails

Under the covers add_reference is just delegating to add_column and add_index so you just need to take care of it yourself:

add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNeilView Question on Stackoverflow
Solution 1 - Ruby on-RailsschpetView Answer on Stackoverflow
Solution 2 - Ruby on-RailsSiaView Answer on Stackoverflow
Solution 3 - Ruby on-RailstoobulkehView Answer on Stackoverflow
Solution 4 - Ruby on-RailsMattView Answer on Stackoverflow
Solution 5 - Ruby on-RailsRichard PeckView Answer on Stackoverflow
Solution 6 - Ruby on-RailsboulderView Answer on Stackoverflow