Rails コース

【応用】多対多のアソシエーション

これまでにmovieプロジェクトで一対多(directorとworks)、および一対一(userとprofile)のアソシエーション について学んできました。ここでは応用編として多対多のアソシエーション を学びましょう。

今回もmovieプロジェクトを題材に、以下の機能を実装します。

・映画作品に複数のタグが付けられるようにする。
・タグをクリックすると、そのタグがついた映画作品の一覧が表示される。

まず前提として、タグの情報を保存するtagsテーブル、およびモデルを作成します。tagsテーブルにはtagの名前が保存できるようにnameというカラムを作成してください。

そのtagsテーブルとworksテーブルのアソシエーションを考えてみましょう。
例えば、上記のStarwarsという作品には、「Japanese」、「Aacademy awards」など複数のタグがついています。そして「Japanese」のタグは、他の映画作品にも使われる可能性がありますので、アソシエーション は多対多の関係性になります。

では、データベースではどのように管理すれば良いでしょうか?
例えば、worksテーブルにtags_idというカラムを追加してtag_idを保存する…と考える方もいらっしゃるかもしれませんが、ひとつ注意点があります。それは「ひとつのカラムには、原則データはひとつだけ」という点です。ですのでこの方法は使えません。

ここで登場するのが「中間テーブル」という考え方です。中間テーブルとは、今回で言えばworksとtagsのテーブルの「中間(真ん中)」にあるイメージで、それぞれのworkとtagのデータがどのように繋がっているかを保存するテーブルです。

ここでは「work_tags」という名前の新しいテーブル(中間テーブル)を作成し、そこにはwork_idとtag_idが保存できるようにします。実際のwork_tagsテーブルはこのようになります。

workのid=1には、3つのタグ(id=2, 4, 5)がついているのが分かります。また、tag_id=1のタグは、3つの作品(id=1, 5, 9)についていますね。タグや作品そのものの情報はtags、worksテーブルに保存し、その繋がりは中間テーブルに保存する、という仕組みです。早速work_tagsテーブルを作成しましょう。

次に、アソシエーションをモデルに定義します。

work.rb(Workモデル)

has_many :work_tags, dependent: :destroy 
has_many :tags, through: :work_tags

tag.rb(Tabモデル)

has_many :work_tags, dependent: :destroy 
has_many :works, through: :work_tags

work_tag.rb(WorkTagモデル)

belongs_to :work
belongs_to :tag

WorkモデルとTagモデルでそれぞれhas_many: work_tagsと定義します。その後ろにdependent: :destroyと記述することで、もしworkやtagのレコードが削除された場合には、関連するwork_tagsテーブル(中間テーブル)のレコードも削除されるようになります。
また、WorkTagモデルでは、belongs_to :workbelongs_to :tag と定義し、WorkモデルとTagモデルと紐付けます。

ここまで作業したら、コンソールからサンプルのタグをいくつか登録してみましょう。
コンソールを使ってデータを登録するには

この教材はメンバー専用です

メンバー専用の教材にアクセスするには、
ハッカーIDでログインしてください。

or