テーブル同士を関連付ける設定
[テーブル同士を関連付けさせる方法]
既存のそれぞれのテーブルに以下のようにマイグレーションファイルを記述し、データベース上で紐付けをする。 (1つのUserに対して複数のBoard[掲示板]を持つように[1:多]の関係になるように、BoardにUserを示す外部キー[user_id]を与える。)
[db/migrateファイル] def up execute 'DELETE FROM boards;' #executeでクエリが実行される。(既存のboardレコードが存在すると紐付くuserが決められず、[user_id]にNOT NULL制約をしていた場合、設定していたNOT NULL制約に引っかかってしまう為、既存のBoardsテーブルを全て削除) add_reference :boards, :user, null: false, foreign_key: true #既存のテーブルに外部キーを追加する。(foreign_keyは、外部キー制約) end def down remove_reference :boards, :user, foreign_key: true end
別な書き方で以下のように記述することもできる。(テーブル作成時に外部キーと外部キー制約を付与)
[db/migrate] def change create_table :boards do |t| t.string :title, null:false t.text :body, null:false t.references :user, :null: false, :foreign_key: true t.timestamps end end
※注意
・外部キー制約を付ける場合は、自動でインデックスが付与される。
・以下のようにreference型を使わない場合は、[foreign_key: true]では外部キー制約にならない。
[db/migrateファイル] def change create_table :boards do |t| t.string :title, null:false t.text :body, null:false t.integer :user_id t.timestamps end add_foreign_key :boards, :users # add_foreign_key :対象のテーブル名, :指定先のテーブル end
[外部キー制約とは?]
1.存在しない値を外部キーとして登録できない。
2.子テーブルの外部キーに値が登録されている場合、親テーブルのレコードは削除できない。(親テーブルだけを削除して子テーブルの外部キーの親が存在しないことを防ぐため)
上記の2の条件を破る方法は、下記のようにモデルに[dependent: :destroy]オプションを指定する。
[app/models] has_many :boards, dependent: :destroy #親モデルが削除された際に、子モデルも一緒に削除する
[関連したモデルのインスタンスを取得する設定方法] 以下のように[1:多]の関係にあるモデルに以下のように記述する。
[app/models] has_many :boards, dependent: :destroy #関連付けさせる先のモデル名を複数形にする。
[app/models] belongs_to :user #関連付けさせる先のモデル名を単数形にする。
※注意
[has_many]や[belongs_to]は、[validates]より上に記載した方が良い。(rubocop規約のため。)
※ポイント
Railsガイドなどには外部キーを付与する際に以下のように[t.belongs_to]と記載があるが、[t.references]と何が違うのか気になったのでこちらにまとめます。
結論、[t.belongs_to]と[t.references]は同じです。
create_table :appointments do |t| t.belongs_to :physician ① t.belongs_to :patient t.datetime :appointment_date t.timestamps end
create_table :appointments do |t| t.references:physician ② t.references:patient t.datetime :appointment_date t.timestamps end
上記のように[t.belongs_to]や[t.references]を使用すると以下のようになる
・[モデル名_id]というカラムを自動生成してくれる
・インデックスを自動で付与してくれる
・外部キー制約は付与してくれないので、付与する場合は[foreign_key:true]を指定する必要がある
また、テーブル作成時に以下のようにターミナルで実行すれば
$ rails g model Appointment physician:references patient:references appointment_date:datetime
以下のようなマイグレーションを1発で生成してくれる。
create_table :appointments do |t| t.belongs_to :physician t.belongs_to :patient t.datetime :appointment_date t.timestamps end
既に存在するテーブルにカラムを追加する際は、以下のようにターミナルで実行することで外部キーを追加できる。
rails g migration AddPhysicianRefToAppointments physician:references
class AddPhysicianRefToAppointments < ActiveRecord::Migration def change add_reference :appointments, :physician, index: true end end
[dependent: destroy]を付与すると、関連づけられたレコードも削除してくれる
ex)
classUser < ApplicationRecord has_many :posts, dependent: :destroy end
classPost < ApplicationRecord belongs_to :user end
上記の場合は、ユーザーを削除すると削除したユーザーに関連づいている投稿も削除される
ex)
class Cooking < ApplicationRecord has_many :cooking_informations has_many :fish, through: :cooking_informations, dependent: :destroy end
class Fish < ApplicationRecord has_many :cooking_informations has_many :cookings, through: :cooking_informations, dependent: :destroy end
class CookingInformation < ApplicationRecord belongs_to :cooking belongs_to :fish end
上記の場合は、魚を削除すると削除した魚に関連づいている中間テーブルのレコードも削除される
上記の場合は、料理を削除すると削除した料理に関連づいている中間テーブルも削除される
上記の書き方だと魚を削除すると削除した魚に関連づいている料理も削除されるような書き方だが、throughを使っている場合は、中間テーブルのレコードのみ削除される
また、上記は以下のように記述することもできる
class Cooking < ApplicationRecord has_many :cooking_informations, dependent: :destroy has_many :fish, through: :cooking_informations end
class Fish < ApplicationRecord has_many :cooking_informations, dependent: :destroy has_many :cookings, through: :cooking_informations end
class CookingInformation < ApplicationRecord belongs_to :cooking belongs_to :fish end
参考記事
Railsの外部キー制約とreference型について - Qiita
外部キーをreferences型カラムで保存する - Qiita
【Ruby on Rails】「外部キー」の設定のされ方 | プログラミングマガジン
【rails】多対多モデルのデータdestroyで出たエラー - Qiita
dependent: :destroyとdependent: :delete_allの違い【Rails】 - 箱のプログラミング日記。
has_many :through時のdependent: :destroyの挙動 - 35歳からの中二病エンジニア
Cannot delete or update a parent row: a foreign key constraint fails【MySQLエラー】 - 箱のプログラミング日記。