Railsで多対多関係を複数つくる
Railsで多対多といえばhas_many trough
とかよく聞くけれど、以下のような関係を作りたいときによくわからなくなったので試しました。
- 映画を表す
Movie
と人物を表すPerson
がある。 Movie#directors
、Movie#actors
で、それぞれ監督と出演者(どっちも複数)がとれる。Person#directed_movies
、Person#performed_movies
で、それぞれ監督作品と出演作品がとれる。Person
は、監督にも出演者にも成り得る。
つまり、Movie
とPerson
の多対多の関係が2本あるってことです。
$ rails g model Movie title:string year:integer $ rails g model Person name:string $ rails g model ActorsRelation movie_id:integer actor_id:integer $ rails g model DirectorsRelation movie_id:integer director_id:integer
class Movie < ActiveRecord::Base has_many :actors_relations has_many :directors_relations has_many :actors, through: :actors_relations has_many :directors, through: :directors_relations end
class Person < ActiveRecord::Base has_many :actors_relations, foreign_key: :actor_id has_many :directors_relations, foreign_key: :director_id has_many :performed_movies, through: :actors_relations, source: :movie has_many :directed_movies, through: :directors_relations, source: :movie end
class ActorsRelation < ActiveRecord::Base belongs_to :movie belongs_to :actor, class_name: "Person" end
class DirectorsRelation < ActiveRecord::Base belongs_to :movie belongs_to :director, class_name: "Person" end
もろもろ
- rspec-railsのバージョンが3になったせいか、見たこと無いエラーが大量に出て焦った。
Gemfile
にrspecとrspec-supportを追記したら動いた。 through:
に指定される、いわゆる中間テーブルの名前が決めづらい。どこかに規約は無いだろうか。person.rb
のhas_many
のうち、下2つのsource: :movie
を書かずにテスト回したら「アソシエーションが見つかりません。has_many :performed_movies, through: :actors_relations, source: <name>
を試すのです。<name>
は:movie
か:actor
です。」という旨のメッセージを迷える子羊の脳内に直接語りかけ、優しく導いてくれた。入信した。