LoginSignup
6
2

More than 5 years have passed since last update.

Railsにおける多対多(n対n)のアソシエーション

Last updated at Posted at 2018-03-26

この記事で前提にしていること

  • Railsアプリを作成できていること
  • ターミナル上の基本的なコマンドを使用できること

注)はじめてのQiitaへの投稿となりますので、不備があるかと思いますが、ご指摘いただけたらと思います。

環境

  • Rails -v 5.1.5
  • Ruby -v 2.4.2

多対多のアソシエーションの作り方

以下のようなモデルを想定します

  • userが複数のgroupをもっている
  • groupが複数のuserをもっている
  • 特定のuserが持っているgroupが呼び出せる
  • 特定のgroupが持っているuserが呼び出せる

モデルの構造

user -- user_group -- groupのように、中間モデルであるuser_groupを設けて、userモデルとgroupモデルを紐付けます

それぞれのモデルの中身は以下のようになるように作ります。

Model column1:class column 2:class
User name:string
Group name:string
UserGroup user_id:integer group_id:integer

コマンド/コード

手順

  • rails g modelでマイグレーションファイルとモデルファイルを生成
  • 上をモデル数分(今回はuser、group、user_groupの3回)繰り返す
  • db:migrateをしてマイグレーションファイルの反映
  • モデルファイルにアソシエーションを記述する

1. マイグレーションファイルとモデルファイルの作成

terminal
bundle exec rails g model User name:string

これを実行することで以下のようなマイグレーションファイルが生成されます。

db/migrate/create_user.rb
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name

      t.timestamps
    end
  end
end

これを作成するモデルごとに行います。
ちなみにuser_groupのマイグレーションファイルは以下のようになります。

db/migrate/create_user_group.rb
class CreateUserGroups < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.integer :user_id
      t.integer :group_id

      t.timestamps
    end
  end
end

マイグレーションファイルの反映

必要なモデルの数だけのマイグレーションファイルが作成できたら以下のコードを走らせて、反映します。

terminal
bundle exec rake db:migrate

モデルファイルにアソシエーションを記述する

まずはuserのモデルファイルuser.rbを以下のように書きます。

app/models/user.rb
class User < ApplicationRecord
  has_many :user_groups
  has_many :groups, through: :user_groups
end

同様にgroupのモデルファイルも書きます。
そして、中間モデルであるuser_groupのモデルファイルには以下のように書きます。

app/models/user_group.rb
class OptionReservation < ApplicationRecord
  belongs_to :user
  belongs_to :group
end

これでrails cでコンソールを立ち上げて、それぞれのモデルにデータを入力したのちに

terminal
User.find(n).groups
あるいは
@user = User.find(n)
@user.groups

などと入力するとuserと紐づいたgroupが引き出せます。

注意事項

ここを書くためにここまで書いてきた節まであります(笑)

HasManyThroughOrderError

モデルファイルを以下のように書くとHasManyThroughOrderErrorが起きます。

app/models/user.rb
class User < ApplicationRecord
  has_many :groups, through: :user_groups
  has_many :user_groups
end

正しくはこうです。

app/models/user.rb
class User < ApplicationRecord
  has_many :user_groups
  has_many :groups, through: :user_groups
end

違いはhas_many: groups, through: :user_groupsが先にあるか、has_many: user_groupsが先にあるかの違いだけです。これはgroup.rbでも同じことが起きます。
私はこれを知らずにうまくアソシエーションを作れないでいました。
当たり前なのかもしれませんが、私にとっては驚きだったので、これで少しでも同じ誤ちを犯す人が少なくなればと思います。

dependent: :delete_all

groupを削除するときにuser_groupも削除されるようにする場合は以下のように書きます。

app/models/group.rb
class User < ApplicationRecord
  has_many :user_groups, dependent: :delete_all
  has_many :users, through: :user_groups
end

has_many :user_groupsの後ろにdependent: :delete_allを書き加えるだけです。

こんなサイトを作っています
ポケモン情報サイトエイパムのしっぽ

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2