【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
このように書きます。