Reputation: 2344
Lets say I have movies, people and movies_people
class Person < ActiveRecord::Base
has_many :movies_people
has_many :movies, through: :movies_people
class Movies < ActiveRecord::Base
has_many :movies_people
has_many :people, through: :movies_people
class MoviesPerson < ActiveRecord::Base
belongs_to :movie
belongs_to :person
end
The table movies_people
has a role attribute, where I want to store the person's job in the movie. Right now I can do things like this in the console:
u = User.first
m = Movie.first
m.people << u
then find the right movies_people entry and set 'role'
retrieving looks like this:
m.people.where(movies_people: {role: :actor})
Whats the best way to:
Upvotes: 1
Views: 521
Reputation: 102174
One solution is to create Roles
which contains a list of existing roles and a MovieRole
class which joins Movies, People and Roles.
class Movie < ActiveRecord::Base
has_many :movie_roles, class_name: "MovieRole"
has_many :roles, through: :movie_roles
has_many :people, through: :movie_roles
end
class People < ActiveRecord::Base
has_many :movie_roles, class_name: "MovieRole"
has_many :movies, through: :movie_roles
has_many :roles, through: :movie_roles
end
class Role < ActiveRecord::Base
has_many :movie_roles, class_name: "MovieRole"
has_many :people, through: :movie_roles
has_many :movies, through: :movie_roles
end
class MovieRole < ActiveRecord::Base
belongs_to :movie
belongs_to :people
belongs_to :role
end
All the relations are stores in movie_roles
which is a three way join table:
class CreateMovieRoles < ActiveRecord::Migration
def change
create_table :movie_roles do |t|
t.references :movie, index: true
t.references :people, index: true
t.references :role, index: true
t.timestamps
end
end
end
Some examples of how you could query this association:
@roles = Movie.find_by(title: 'Godzilla').roles
@directors = People.joins(:roles).where(roles: {name: 'Director'})
@directed_by_eastwood = Movie.joins(:people, :roles)
.merge(Role.where(name: 'Director'))
.merge(People.where(name: 'Clint Eastwood'))
To associate a person with a movie you would:
MovieRole.create(people: person, movie: movie, role: role)
But you will want to setup convinience methods like:
class People < ActiveRecord::Base
def add_role(role, movie)
role = Role.find_by(name: role) unless role.is_a?(Role)
MovieRole.create(people: self, movie: movie, role: role)
end
end
class Movie < ActiveRecord::Base
def add_cast(role, person)
role = Role.find_by(name: role) unless role.is_a?(Role)
MovieRole.create(people: person, movie: self, role: role)
end
end
Upvotes: 3
Reputation: 5229
To save the role, just:
person = Person.find(person_id)
movie = Movie.find(movie_id)
movie.movies_people.create(person: person, role: :actor)
To retrieve by role,
movie = Movie.includes(:people).where(id: movie_id).where(movies_people: {role: :actor})
Edit: I don't advice to add a roles table, unless you need it. I follow the agile principles, in this case: "Simplicity--the art of maximizing the amount of work not done--is essential."
Upvotes: 1