
【Rails】マイグレーションファイルで外部キー、インデックスを設定する方法
はじめに
この記事の概要
こんにちは、Rubyをメインで書いている株式会社TOKOSのツキヤです!
今回は、マイグレーションファイル内で外部キー制約やインデックスの設定をする方法を説明します!
$ rails g model Userや$ rails g migrationで作成される20220711134216_create_users.rbのようなファイルのことだね!
外部キー制約とは?
外部キー制約とは、外部キーに対して親テーブルのIDとして存在するものしか指定できないようにする制約のことです。
usersテーブルとtweetsテーブルが1対多の関係だとすると、下記画像のような関係になります!
外部キー制約を指定することで、データの整合性をより正確にできます😎
インデックスとは?
超簡単に説明すると、カラムに指定すると、データを取得する時に早くなるものです!
また、ユニーク(一意)制約をかけることも可能です!
ここで詳しく説明するとかなり長くなってしまうので、詳しく知りたい方はググってみてください🙇♂️
対象読者
- マイグレーションファイルに外部キー制約を指定したい方
- マイグレーションファイルにインデックス・ユニーク制約を指定したい方
外部キー系
references
よく使用されるreferencesには、下記のようにforeign_key: trueを入れるだけで完了です!
class CreateTweets < ActiveRecord::Migration[7.0]
def change
create_table :tweets do |t|
t.references :user, null: false, foreign_key: true
t.string :body, null: false
t.timestamps
end
end
end今まで入れてなかった人は、とりあえず書いておいた方が無難です!
foreign_key: trueが無くても、モデルファイル(user.rb)でhas many tweetsとすればリレーション自体は可能だけど、意図しない値の混入を防ぐためにも設定しておこう!
また、外部キー制約を詳細にカスタマイズすることもできます!
class CreateTweets < ActiveRecord::Migration[7.0]
def change
create_table :tweets do |t|
t.references :user, null: false, foreign_key: { on_update: :cascade, on_delete: :cascade }
t.string :body, null: false
t.timestamps
end
end
endon_updateは、親のID(この場合はuser.id)が変更された場合にどうするかの挙動を決めています。
:cascadeは、親の変更に合わせるという意味です!他にも :nullify(NULLを設定)や :restrict(親の変更を禁止)などがあります。
on_deleteも同様に、親のIDが削除された際の挙動の設定です!
後から設定する場合
テーブル作成の際に忘れてしまった場合や、後で制約を変更したい場合は下記のように記述すればOKです!
class AddForeignKeyTweets < ActiveRecord::Migration[7.0]
def change
add_foreign_key :tweets, :users, column: :user_id, on_update: :cascade, on_delete: :cascade
end
end逆に削除したい場合は下記のような記述です!
class RemoveIndexTweets < ActiveRecord::Migration[7.0]
def change
remove_foreign_key :tweets, :users, column: :user_id
end
endadd_foreign_keyかremove_foreign_keyの違いだね!
インデックス系
基本形
インデックスに関しては、つけたいカラムに対してindex: trueと記述するだけです!
class CreateTweets < ActiveRecord::Migration[7.0]
def change
create_table :tweets do |t|
t.references :user, null: false, foreign_key: true
t.string :body, null: false, index: true
t.timestamps
end
end
endreferencesの場合は、自動でインデックスが付いているので特になにもしなくてOKです!
ユニーク制約
ここからはユニーク制約です!
テーブル設計の際に、has_one(1対1)の実装をする時があると思います。
この際に同じ親IDをもつレコードは要らないのでユニーク制約を下記のように設定します!
class CreateUserInformations < ActiveRecord::Migration[7.0]
def change
create_table :user_informations do |t|
t.references :user, null: false, foreign_key: true, index: { unique: true }
t.string :body, null: false
t.timestamps
end
end
endindex: { unique: true }の部分ですね!
こうするだけでユニーク制約がかかります!
複合ユニーク制約
お次は複数のカラムに対するユニーク制約です!
例えば、お気に入り機能を実装する際に「同じユーザーが1つの投稿に2回以上お気に入りをさせない」場合に必要かと思います!
class CreateFavorites < ActiveRecord::Migration[7.0]
def change
create_table :favorites do |t|
t.references :user, null: false, foreign_key: true
t.references :tweet, null: false, foreign_key: true
t.timestamps
end
add_index :favorites, [:user_id, :tweet_id], unique: true
end
end9行目のような記述をすることで、複合ユニーク制約の実装ができます!
2つのカラムにまたがる設定のため、別途設定のための文書を記述してあげる必要があります!
後から設定する場合
こちらも外部キー制約と同じく下記の様な形になります!
削除もまとめて書いちゃいます!
class AddIndexTweets < ActiveRecord::Migration[7.0]
def up
add_index :favorites, [:user_id, :tweet_id], unique: true
end
def down
remove_index :favorites, [:user_id, :tweet_id], unique: true
end
end終わりに
今回はモデルファイルでは無く、DB側での制約を紹介しました!
ユニーク制約の場合、アプリケーション側(モデルファイル)で同様のバリデーションをかけないとエラーになってしまいますので注意してください(ex: validates :column_name, uniqueness: true)
アプリケーション側でちゃんとvalidationをかけることも大事ですが、DB側でも今回のような制約を加えることでより堅牢なサービスを目指しましょう😆
Railsの勉強をしたい方はこの本が参考になるかと思いますので興味がある方はぜひ読んでみてください🙇♂️

