この記事で前提にしていること
- 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. マイグレーションファイルとモデルファイルの作成
bundle exec rails g model User name:string
これを実行することで以下のようなマイグレーションファイルが生成されます。
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
これを作成するモデルごとに行います。
ちなみにuser_groupのマイグレーションファイルは以下のようになります。
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
###マイグレーションファイルの反映
必要なモデルの数だけのマイグレーションファイルが作成できたら以下のコードを走らせて、反映します。
bundle exec rake db:migrate
モデルファイルにアソシエーションを記述する
まずはuserのモデルファイルuser.rbを以下のように書きます。
class User < ApplicationRecord
has_many :user_groups
has_many :groups, through: :user_groups
end
同様にgroupのモデルファイルも書きます。
そして、中間モデルであるuser_groupのモデルファイルには以下のように書きます。
class OptionReservation < ApplicationRecord
belongs_to :user
belongs_to :group
end
これでrails c
でコンソールを立ち上げて、それぞれのモデルにデータを入力したのちに
User.find(n).groups
あるいは
@user = User.find(n)
@user.groups
などと入力するとuserと紐づいたgroupが引き出せます。
注意事項
ここを書くためにここまで書いてきた節まであります(笑)
HasManyThroughOrderError
モデルファイルを以下のように書くとHasManyThroughOrderError
が起きます。
class User < ApplicationRecord
has_many :groups, through: :user_groups
has_many :user_groups
end
正しくはこうです。
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も削除されるようにする場合は以下のように書きます。
class User < ApplicationRecord
has_many :user_groups, dependent: :delete_all
has_many :users, through: :user_groups
end
has_many :user_groups
の後ろにdependent: :delete_all
を書き加えるだけです。
こんなサイトを作っています
ポケモン情報サイトエイパムのしっぽ