Ruby on Rails - n:m many-to-many relation - ruby-on-rails

I have two objects i.e. recipe & ingredient.
I have created them using
rails generate scaffold ingredient id:integer name:string
rails generate scaffold recipe id:integer name:string
I want too create them with a n:m relation (many-to-many).
How do I do it? Should I create a different scaffold?
Thanks.

No, you need to create a join table, by generatin a migration.
rails g migration ingredients_recipes ingredient_id:integer recipient_id:integer
Then, you can add to your models:
Ingredient.rb
has_and_belongs_to_many :recipe
Recipe.rb
has_and_belongs_to_many :ingredients
Or if you want to add other properties to the connection (e.g. Quantity), then you may generate a model for it.
rails g model ingredients_recipes ingredient_id:integer recipient_id:integer

You can execute rails generate migration create_join_table_ingredient_recipe ingredient recipe which will generate a migrations file, which you can modify to include indices and foreign keys, like:
class CreateJoinTableIngredientRecipe < ActiveRecord::Migration
def change
create_join_table :ingredients, :recipes do |t|
t.index [:ingredient_id, :recipe_id]
t.index [:recipe_id, :ingredient_id]
end
add_foreign_key :ingredients_recipes, :ingredients
add_foreign_key :ingredients_recipes, :recipes
end
end
Then you can add in models/ingredient.rb:
has_and_belongs_to_many :recipe
and conversely in models/recipe.rb:
has_and_belongs_to_many :ingredients

Here is a great tutorial which shows two methods for using many_to_many relationships : http://railscasts.com/episodes/47-two-many-to-many

Related

create a new table with one-to-many relationshiop

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

Storing only the id of two related tables

Like ownership.
Suppose I already have two models:
person name:string
dog name:string
Now I need to have the third table - ownership.
In a normal relational database, I only need to store the two ids from those tables, but rails automatically generates them. So how can I reference them?
rails generate model Ownership XXXXXXXX
You can set up a relationship model, like this:
rails generate model ownerships person:references dog:references
rails db:migrate
app/models/ownership.rb:
class Ownership < ApplicationRecord
belongs_to :person
belongs_to :dog
end
app/models/person.rb:
class Person < ApplicationRecord
has_many :ownerships
has_many :dogs, through: :ownerships
end
Now you can do:
john = Person.create(name: 'John')
doggy = Dog.create(name: 'Doggy')
john.dogs << doggy
And you just added a dog to John's ownerships. You can find them like this:
puts john.dogs.first.name
# => "Doggy"
If you look at your generated schema.rb:
create_table "ownerships", force: :cascade do |t|
t.integer "person_id" # Here are your two ids
t.integer "dog_id" #
# ...
I would recommend you going through Rails Tutorial by Michael Hartl. In the last section he implements a relationship model with very thorough explanation.
What you want is a has_ :through relationship or a has_and_belongs_to_many relationship. Check the docs for more info (you can find detailed explanation on how to set up the DB and relationship.)It is a standard issue in rails:
http://guides.rubyonrails.org/association_basics.html

Scaffolding table obtained by many-to-many relationship

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

Possible to add a column to an ActiveRecord model that is a foreign key?

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.

add associations to exisiting models

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

Resources