Home > Enterprise >  Rails self join with has_many association
Rails self join with has_many association

Time:10-23

I am trying to write an app like IMDB in rails.

I have created the Movie model. Every movie has many movie recommendations (which are also instances of Movie).

I don't know how to add the "has_many" association, how to write the migration file or how to add recommended movies to each movie.

CodePudding user response:

You have a many-to-many relationship, which means we need a join table Recommendation.

Create model and migration files with a generator:

bin/rails generate model Movie
bin/rails generate model Recommendation

Then update migrations:

# db/migrate/20221023063944_create_movies.rb
class CreateMovies < ActiveRecord::Migration[7.0]
  def change
    create_table :movies do |t|
      # TODO: add fields
    end
  end
end

# db/migrate/20221023064241_create_recommendations.rb
class CreateRecommendations < ActiveRecord::Migration[7.0]
  def change
    create_table :recommendations do |t|
      t.references :movie,             null: false, foreign_key: true
      t.references :recommended_movie, null: false, foreign_key: { to_table: :movies }
    end
  end
end

Run migrations:

bin/rails db:migrate

Setup models:

# app/models/movie.rb
class Movie < ApplicationRecord
  # NOTE: this is the relationship for join table
  has_many :recommendations, dependent: :destroy

  # NOTE: get movies from join table
  has_many :recommended_movies, through: :recommendations
  #       this ^ is the name of the relationship in `Recommendation` we want
end

# app/models/recommendation.rb
class Recommendation < ApplicationRecord
  belongs_to :movie

  # NOTE: our foreign key is `recommended_movie_id` which rails infers 
  #       from `:recommended_movie`, but we have to specify the class:
  belongs_to :recommended_movie, class_name: "Movie"
end

Test it in the console bin/rails console:

>> 3.times { Movie.create }
>> Movie.first.recommended_movies << [Movie.second, Movie.third]
>> Movie.first.recommended_movies
=> [#<Movie:0x00007f15802ec4c0 id: 2>, #<Movie:0x00007f15802ec3d0 id: 3>]

or like this:

>> Movie.second.recommendations << Recommendation.new(recommended_movie: Movie.first)
>> Movie.second.recommended_movies
=> [#<Movie:0x00007f158215ef20 id: 1>]

https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

https://guides.rubyonrails.org/association_basics.html#self-joins

CodePudding user response:

When creating a migration you need to define which model refrence you want to assign

create_table :student do |t|
 t.references :class, foreign_key: true
end

here i am telling my class table to store primary key of student as foreign key after migration there will be a column in class named student_id which stores pk of student table. Then i will define association in class model file

class student < ApplicationRecord
  belongs_to :class
end

This will help me in query so i can write

student= Student.find 'student_id'
class = student.class

This will return the class of that student. For has_many the procedure is same but it will return you the array

  • Related