文系人間がエンジニアを目指すブログ

大学の文系学部を卒業した私が、ソフトウェアエンジニアになって、一人前を目指す過程を書くブログです。

【Rails】多対多のアソシエーションの組み方をまとめてみた。

そもそも、アソシエーションてなに?

アソシエーションというのは、モデルとモデルの関係性のことです。

例えば、twitterのような、tweetモデルとuserモデルを持っているアプリケーションを例に取ってみましょう。

userは、複数のtweetを投稿することができ、データベース上では、一人のuserが複数のtweetを持っている状態にあります。

反対に、tweetは一人のユーザーによってのみ作られますから、データベース上では、一つのtweetが一人のuserを持っている状態にあります。

このような、モデル同士の関係のことをアソシエーションと呼びます。

アソシエーションを組むと何ができるようになるの?

アソシエーションを組むと、関連付けたモデルのデータを取得でき、大変便利です。

というか、アソシエーションなしでは複雑なアプリケーションは作れないので、必須の機能と言っても過言ではありません。

例えば、tweetを表示する際に、tweetに紐づいたuserのデータを取得できるので、「誰がこのtweetをしたのか」ということを簡単に表示させることができます。

多対多のアソシエーションってなに?

では、多対多のアソシエーションとは何でしょうか?

先ほどのtweetの例では、

という関係でしたが、この関係性が複数x複数に変わります。

userモデルとgroupモデルを持っているようなアプリケーションを例に考えます。

userの視点から見ると、userは複数のgroupに所属することができます。

また、groupの視点から見ても、groupは複数の所属userを持っているような状態になります。

  • user => 複数のgroups
  • group => 複数のusers

という関係性になります。これが多対多のアソシエーションです。

実装の手順

それでは、実際にuserモデルとgroupモデルの多対多の関係を作っていきましょう!!

ちなみに、今回の実装環境は、

ruby '2.3.1'
gem 'rails', '~> 5.1.6'

となっております。

多対多のアソシエーションを作りたい2つのモデルを作成

まずは、多対多の関係を組む2つのモデルを作成。

bundle exec rails g model user
bundle exec rails g model user

カラム名や制約を適宜追加して、

bundle exec rake db:migrate

しましょう!

2つのモデルをつなぐ、中間モデルを作成

userモデルとgroupモデルの二つを繋ぐ、中間モデルを作成します。

bundle exec rails g model member

Migrationファイルが作成されるので、中身を変更していきますが、ここで注意点があります。

【Caution!!】中間テーブルのMigrationファイルでの、reference型の外部キーの設定方法

「モデルを作成したら、いざテーブルを作ろう!」ということで、Migrationファイルを編集。

ここで、

create_table :members do |t|
    t.references           :user_id, null: false, foreign_key: true
    t.references           :group_id, null: false, foreign_key: true
    t.timestamps
end

このように、カラム名を「xx_id」みたいな形で書いてしまうと、

Mysql2::Error: Cannot add foreign key constraint

とか、

ActiveRecord::StatementInvalid: Mysql2::Error: Table 'CB名.テーブル名' doesn't exist

って出てしまいますので、

create_table :members do |t|
    t.references           :user, null: false, foreign_key: true
    t.references           :group, null: false, foreign_key: true
    t.timestamps
end

このように、「_id」はなしで書きましょう。

references型を指定するときは、「_id」を書いてなくても、「user_id」とか「group_id」というカラム名で登録してくれます。

integer型で外部キーを設定する方法に慣れている人は間違えやすいと思うので、気をつけましょう。

3つのモデルのアソシエーションを組む

今作った3つのモデルのアソシエーションを組んでいきます。

ポイントは、「中間テーブルを通じた多対多の関係をどう書くのか?」という点です。

user.rb(多対多の関係を組むモデル)

has_many :groups, through: :members
has_many :members

group.rb(多対多の関係を組むモデル)

has_many :users, through: :members
has_many :members

member.rb(中間モデル)

belongs_to :user
belongs_to :group

このように書きます。