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

  • 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を書き加えるだけです。

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.