I'm wondering how I can add associations to my models. Suppose, I generate two models
rails generate model User
rails generate model Car
Now I want to add an associations so that the models acquire the form
class User < ActiveRecord::Base
has_many :cars
end
class Car < ActiveRecord::Base
belongs_to :user
end
The question is: how to apply this modification by migrations in order to obtain cars_users table in the database? I'm planning to use that table in my code.
belongs_to association expect an association_id column in its corresponding table. Since cars belongs_to user, the cars table should have a user_id column. This can be accomplished 2 ways.
first, you can generate the column when you create the model
rails g model car user_id:references
or just add the user_id after you create the model like Richard Brown's answer. Be careful that if you use integer instead of references, you'd have to create the index yourself.
rails g migration add_user_id_to_cars user_id:integer
then in the generated migration, add
add_index :cars, :user_id
UPDATE:
As Joseph has mentioned in the comments, the need to add the index manually has already been addressed in the current version of Rails. I think it was introduced in Rails 4. You can read more of it in the official Rails guide for migrations. The gist of it is running the following generator
bin/rails g migration add_user_to_cars user:references
will create a migration with a line similar to
add_reference :cars, :user, index: true
This will add a user_id column to the cars table and it will also mark that column to be indexed.
Following #jvnill's explanation in rails 4 (and maybe in rails 3.2 too) you can do it like this too (avoiding the id parts and remembering the exact convetions):
rails g migration AddUserToCar user:references
Which will create the following migration, taking care of both adding the column and index with all correct conventions:
class AddUserToCar < ActiveRecord::Migration
def change
add_reference :cars, :user, index: true
end
end
At the end as always run the migration:
rake db:migrate
View your schema.rb to view the new index and user_id column.
Generate a migration to create the association:
rails g migration AddUserIdToCars user_id:integer
rake db:migrate
Migration file:
class Createuser < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
end
create_table :cars do |t|
t.belongs_to :user, index: true
t.varchar(255) :model
t.varchar(255) :color
end
end
end
Related
I have an old rails project using Rails 2 . There is already model class Student. In database, there is a table students. Now, I need to implement that each student can have multiple courses. Which means I need to have a new table in database that's courses table & have one-to-many relationship from student to course.
How to create migration file to make this?
Rails 2 didn't have an option to create associations via the migration generator, so you have to take a more manual approach.
You can create the migration thusly: https://www.tutorialspoint.com/ruby-on-rails-2.1/rails-migrations.htm
You'll need to add the column student_id to your courses table with a column type of integer
Then add the following to your Student model:
has_many :courses
This shouldn't be too difficult if you are actually using Rails 2.3
And TBH if you aren't AT LEAST on 2.3, then you should probably just recreate this project entirely...
1.) Use ruby script/generate model Course name:string description:text student_id:bigint to generate your migration, which should look something like this:
class CreateCourses < ActiveRecord::Migration
def self.up
create_table :courses do |t|
t.string :name
t.text :description
t.bigint :student_id
t.timestamps
end
end
def self.down
drop_table :courses
end
end
2.) Find the newly created MODEL in your project directory with name course and add the association to the file:
belongs_to :student
3.) Find the STUDENT model in your project folder and add the has_many association to that:
has_many :students
4.) In your terminal, cd into your project folder and run rake db:migrate
You should be good to go after that! Here's the reference for Rails 2.3 associations: https://guides.rubyonrails.org/v2.3/association_basics.html
I have two models: User and Club and I have a many-to-may relationship like:
user has_and_belongs_to_many clubs
club has_and_belongs_to_many users
Now, I will have a third table with the result of this relationship:
user_clubs(user_id, club_id)
When generating scaffold, for example, rails generate scaffold User name:string birth_date:date
gender:string login_id:integer, how can I generate that relationship? In the same way?
Thank you
You need to use generate migration
rails g migration create_club_users user_id:integer club_id:integer
It will create the following migration:
class CreateClubUsers < ActiveRecord::Migration
def change
create_table :club_users do |t|
t.integer :user_id
t.integer :club_id
end
end
end
Then you should set id to false as in Create an ActiveRecord database table with no :id column?
I suggest that you read WHY YOU DON’T NEED HAS_AND_BELONGS_TO_MANY RELATIONSHIPS.
You can try this:
rails g migration CreateClubsUsersJoinTable club_id:integer user_id:integer
You should follow alphabetical order of models when creating join table of has_and_belongs_to_many association in rails
Please refer this link for further assistance
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
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
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
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
class MigrationName < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_reference :uploads, :user, index: {algorithm: :concurrently}
end
end
I can easily create a scaffold or model in Rails with a field that is a reference (foreign key) to another model:
rails g model Cat owner:references
rails g scaffold Cat owner:references
But I can't seem to do the same for adding a column in a migration:
rails g migration AddOwnerToCats owner:references
What the above does is produce a migration file like this:
class AddOwnerToCats < ActiveRecord::Migration
def change
add_column :cats, :owner, :references
end
end
And when I try and run it with rake db:migrate, I get this:
SQLite3::SQLException: near "references": syntax error: ALTER TABLE "cats" ADD "owner" references
So is there a way to add a column that is a reference to another model? Or do I just have to do:
rails g migration AddOwnerToCats owner_id:integer
And then go into the migration and add an index for owner_id?
One way, which i can think of, and is a workaround is, simply create the column, and give the join condition in the model. i.e.
has_many :x, :class_name => y, :foreign_key => z
I know it's an old question, but as of Rails 5 (and perhaps earlier versions), the official docs have been updated:
rails generate migration AddOwnerRefToCats owner:references
will generate
class AddOwnerRefToCats < ActiveRecord::Migration[5.0]
def change
add_reference :cats, :owner, foreign_key: true
end
end
You can also add , index: true to create an index.
If I create a new rails 3 migration with (for example)
rails g migration tester title:tester user:references
, everything works fine...however if I add a column with something along the lines of:
rails g migration add_user_to_tester user:references
the reference field is not recognised. In short, the question is: how do I add a referencing column to a rails migration from the command line?
If you are using the Rails 4.x you can now generate migrations with references, like this:
rails generate migration AddUserRefToProducts user:references
like you can see on rails guides
EDIT: This is an outdated answer and should not be applied for Rails 4.x+
You don't need to add references when you can use an integer id to your referenced class.
I'd say the advantage of using references instead of a plain integer is that the model will be predefined with belongs_to and since the model is already created and will not be affected when you migrate something existing, the purpose is kind of lost.
So I would do like this instead:
rails g migration add_user_id_to_tester user_id:integer
And then manually add belongs_to :user in the Tester model
Please note that you will most likely need an index on that column too.
class AddUserReferenceToTester < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer
add_index :testers, :user_id
end
end
With the two previous steps stated above, you're still missing the foreign key constraint. This should work:
class AddUserReferenceToTester < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer, references: :users
end
end
You can use references in a change migration. This is valid Rails 3.2.13 code:
class AddUserToTester < ActiveRecord::Migration
def change
change_table :testers do |t|
t.references :user, index: true
end
end
def down
change_table :testers do |t|
t.remove :user_id
end
end
end
c.f.: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_table
Running rails g migration AddUserRefToSponsors user:references will generate the following migration:
def change
add_reference :sponsors, :user, index: true
end
When adding a column you need to make that column an integer and if possible stick with rails conventions. So for your case I am assuming you already have a Tester and User models, and testers and users tables.
To add the foreign key you need to create an integer column with the name user_id (convention):
add_column :tester, :user_id, :integer
Then add a belongs_to to the tester model:
class Tester < ActiveRecord::Base
belongs_to :user
end
And you might also want to add an index for the foreign key (this is something the references already does for you):
add_index :tester, :user_id
That will do the trick:
rails g migration add_user_to_tester user_id:integer:index
You can add references to your model through command line in the following manner:
rails g migration add_column_to_tester user_id:integer
This will generate a migration file like :
class AddColumnToTesters < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer
end
end
This works fine every time i use it..
For Rails 4
The generator accepts column type as references (also available as belongs_to).
This migration will create a user_id column and appropriate index:
$ rails g migration AddUserRefToProducts user:references
generates:
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_reference :products, :user, index: true
end
end
http://guides.rubyonrails.org/active_record_migrations.html#creating-a-standalone-migration
For Rails 3
Helper is called references (also available as belongs_to).
This migration will create a category_id column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the _id for you.
change_table :products do |t|
t.references :category
end
If you have polymorphic belongs_to associations then references will add both of the columns required:
change_table :products do |t|
t.references :attachment, :polymorphic => {:default => 'Photo'}
end
Will add an attachment_id column and a string attachment_type column with a default value of Photo.
http://guides.rubyonrails.org/v3.2.21/migrations.html#creating-a-standalone-migration