add_foreign_key vs add_reference in rails

Viewed 5601

What is the difference between "add_foreign_key" and "add_reference" methods in rails?

According to rails official guide all I understand is that they both are used to create foreign key constraint between two tables.

3 Answers

add_foreign_key - adds a new foreign key. from_table is the table with the key column, to_table contains the referenced primary key.

add_reference - is meant as a shortcut for creating a column, index and foreign key at the same time.

What is foreign key - a foreign key is a field or group of fields in a table that uniquely identifies a row in another table.

(Note: This answer is based on Rails 6.0.)

In a word, add_reference (Ref) is kind of a short-form of a combined set of add_column, add_index, and add_foreign_key (Ref) without adding a DB-level foreign key in default. So, when you want to achieve something simple enough or (conversely?) a polymorphic reference, add_reference is handy. If not, use add_foreign_key, maybe combined with explicit add_index.

As a simple example, these two are (I think) equivalent to each other:

add_reference   :articles, :author, foreign_key: true

add_column      :articles, :author_id, :bigint, null: true
add_foreign_key :articles, :authors
add_index       :articles, :author_id

Here are more detailed differences:

  1. The second argument of add_reference is a reference (column name without _id, hence usually singular), whereas that of add_foreign_key is a table name (hence usually plural).
  2. In add_reference,
    1. DB-level foreign key is not created in default, unless foreign_key option is specified non-nil.
    2. index: true is default, whereas the index is irrelevant in add_foreign_key
    3. null: true is default (allowing nulls for the column), which is irrelevant in add_foreign_key
  3. polymorphic: true is available only with add_reference in Rails (which will create 2 columns in one action; see Ref).
  4. The formats of the accepted options between the two are totally different, though add_reference is largely more inclusive, accepting a wider range of options.

Two example realistic use-cases

For the has_one association, where null is forbidden:

add_reference :products, :merchant, null: false, index: {unique: true}, foreign_key: {on_delete: :cascade}

When a table has 2 foreign-key columns to an identical table:

add_foreign_key :products, :merchants, column: :seller_id
add_foreign_key :products, :merchants, column: :buyer_id
add_index :products, [:seller_id, :buyer_id], unique: true, name: 'index_my_name_shorter_than_64chars'

There is a limitation to add_reference compared to add_foreign_key.

As I am curious if there is a way to do the exact following thing with add_reference. Afaik the standard foreign_key to primary_key/reference_key mapping can not be diverged with add_reference.

Migration snippet

add_foreign_key :foos, :bars, column: :foo_key, primary_key: :foo_key, type: :string 
add_index :foos

Usecase is when trying to map the foreign_key to the primary_key in a non Standard way. Let's say using a table with STI to hold multiple references

Related