Neil
Neil

Reputation: 5178

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

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?

Upvotes: 87

Views: 75658

Answers (7)

Syamlal
Syamlal

Reputation: 910

foreign key with different column name

add_reference(:products, :supplier, foreign_key: { to_table: :firms })

refer the documentation Docs

Upvotes: 0

schpet
schpet

Reputation: 10656

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}

Upvotes: 160

Matt
Matt

Reputation: 14048

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

Upvotes: 11

toobulkeh
toobulkeh

Reputation: 1616

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

Upvotes: 12

Sia
Sia

Reputation: 9221

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.

Upvotes: 85

Richard Peck
Richard Peck

Reputation: 76784

# 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

Upvotes: 7

boulder
boulder

Reputation: 3276

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

Upvotes: 0

Related Questions