A user has many uploads. I want to add a column to the uploads
table that references the user
. What should the migration look like?
Here is what I have. I'm not sure if I should use (1) :user_id, :int
or (2) :user, :references
. I'm not even sure if (2) works. Just trying to do this the "rails" way.
class AddUserToUploads < ActiveRecord::Migration
def change
add_column :uploads, :user_id, :integer
end
end
Relevant question except for Rails 3. Rails 3 migrations: Adding reference column?
Rails 4.x
When you already have users
and uploads
tables and wish to add a new relationship between them.
All you need to do is: just generate a migration using the following command:
rails g migration AddUserToUploads user:references
Which will create a migration file as:
class AddUserToUploads < ActiveRecord::Migration
def change
add_reference :uploads, :user, index: true
end
end
Then, run the migration using rake db:migrate
. This migration will take care of adding a new column named user_id
to uploads
table (referencing id
column in users
table), PLUS it will also add an index on the new column.
UPDATE [For Rails 4.2]
Rails can’t be trusted to maintain referential integrity; relational databases come to our rescue here. What that means is that we can add foreign key constraints at the database level itself and ensure that database would reject any operation that violates this set referential integrity. As @infoget commented, Rails 4.2 ships with native support for foreign keys(referential integrity). It's not required but you might want to add foreign key(as it's very useful) to the reference that we created above.
To add foreign key to an existing reference, create a new migration to add a foreign key:
class AddForeignKeyToUploads < ActiveRecord::Migration
def change
add_foreign_key :uploads, :users
end
end
To create a completely brand new reference with a foreign key(in Rails 4.2), generate a migration using the following command:
rails g migration AddUserToUploads user:references
which will create a migration file as:
class AddUserToUploads < ActiveRecord::Migration
def change
add_reference :uploads, :user, index: true
add_foreign_key :uploads, :users
end
end
This will add a new foreign key to the user_id
column of the uploads
table. The key references the id
column in users
table.
NOTE: This is in addition to adding a reference so you still need to create a reference first then foreign key (you can choose to create a foreign key in the same migration or a separate migration file). Active Record only supports single column foreign keys and currently only mysql
, mysql2
and PostgreSQL
adapters are supported. Don't try this with other adapters like sqlite3
, etc. Refer to Rails Guides: Foreign Keys for your reference.
Rails 5
You can still use this command to create the migration:
rails g migration AddUserToUploads user:references
The migration looks a bit different to before, but still works:
class AddUserToUploads < ActiveRecord::Migration[5.0]
def change
add_reference :uploads, :user, foreign_key: true
end
end
Note that it's :user
, not :user_id
Local::User
instead of User
do something like rails g migration AddLocalUserToUploads user:references
.
:index
t.index ["user_id"], name: "index_uploads_on_user_id", using: :btree
belongs_to :user
in Upload
class, so we can use upload.user
to get the user instance.
if you like another alternate approach with up
and down
method try this:
def up
change_table :uploads do |t|
t.references :user, index: true
end
end
def down
change_table :uploads do |t|
t.remove_references :user, index: true
end
end
Create a migration file
rails generate migration add_references_to_uploads user:references
Default foreign key name
This would create a user_id column in uploads table as a foreign key
class AddReferencesToUploads < ActiveRecord::Migration[5.2]
def change
add_reference :uploads, :user, foreign_key: true
end
end
user model:
class User < ApplicationRecord
has_many :uploads
end
upload model:
class Upload < ApplicationRecord
belongs_to :user
end
Customize foreign key name:
add_reference :uploads, :author, references: :user, foreign_key: true
This would create an author_id column in the uploads tables as the foreign key.
user model:
class User < ApplicationRecord
has_many :uploads, foreign_key: 'author_id'
end
upload model:
class Upload < ApplicationRecord
belongs_to :user
end
add_foreign_key :from_table, :to_table, column: :field primary_key: "row_id"
You might also want to index it, for performance reasons
Just to document if someone has the same problem...
In my situation I've been using :uuid
fields, and the above answers does not work to my case, because rails 5 are creating a column using :bigint
instead :uuid
:
add_reference :uploads, :user, index: true, type: :uuid
Reference: Active Record Postgresql UUID
[Using Rails 5]
Generate migration:
rails generate migration add_user_reference_to_uploads user:references
This will create the migration file:
class AddUserReferenceToUploads < ActiveRecord::Migration[5.1]
def change
add_reference :uploads, :user, foreign_key: true
end
end
Now if you observe the schema file, you will see that the uploads table contains a new field. Something like: t.bigint "user_id"
or t.integer "user_id"
.
Migrate database:
rails db:migrate
Another syntax of doing the same thing is:
rails g migration AddUserToUpload user:belongs_to
Success story sharing
rails g migration AddUserToUploads user:references
producesadd_reference :uploads, :user, index: true, foreign_key: true
in the appropriate migration....index: true, foreign_key: true
instead o lineadd_foreign_key
.foreign_key
andt.reference
? Isn'tt.reference
essentially equivalent toforiegn_key
+index
?