mmag

ハマったことメモなど

Railsで多対多関係を複数つくる

Railsで多対多といえばhas_many troughとかよく聞くけれど、以下のような関係を作りたいときによくわからなくなったので試しました。

  • 映画を表すMovieと人物を表すPersonがある。
  • Movie#directorsMovie#actorsで、それぞれ監督と出演者(どっちも複数)がとれる。
  • Person#directed_moviesPerson#performed_moviesで、それぞれ監督作品と出演作品がとれる。
  • Personは、監督にも出演者にも成り得る。

つまり、MoviePersonの多対多の関係が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になったせいか、見たこと無いエラーが大量に出て焦った。Gemfilerspecrspec-supportを追記したら動いた。
  • through:に指定される、いわゆる中間テーブルの名前が決めづらい。どこかに規約は無いだろうか。
  • person.rbhas_manyのうち、下2つのsource: :movieを書かずにテスト回したら「アソシエーションが見つかりません。has_many :performed_movies, through: :actors_relations, source: <name>を試すのです。<name>:movie:actorです。」という旨のメッセージを迷える子羊の脳内に直接語りかけ、優しく導いてくれた。入信した。