【Rails】referencesの使い方・外部キー制約/index/null: false
この記事の対象者
- referencesってなに?って方
- 外部キー(foreign_key)について知りたい方
- foreign_key: true・null: false・indexについて知りたい方
目次
外部キー(foreign_key)の説明
referencesとはカラムに保存出来る型の1つで、外部キー(foreign_key)を作成する際に使用します。
そのため、referencesの説明をする前に外部キー(foreign_key)の説明を簡単にしたいと思います。
簡単に言うと外部キー(foreign_key)とは、主キー(primary key)を参照するためのカラムのことです。
もうちょっと詳しくusersテーブル(親)とpostsテーブル(子)を例に説明します。
usersテーブル(親)
id | name |
1 | 佐藤 |
2 | 鈴木 |
3 | 高橋 |
postsテーブル(子)
id | body | user_id |
1 | 買い物に行きました | 2 |
2 | たくさん勉強をした! | 2 |
3 | ずっと寝てました | 1 |
このように2つのテーブルがあり、1人のuserが複数のpostを持っている(1対多)のアソシエーションを組んでいるとします。
その場合、どのuserが投稿したpostなのかを区別するために外部キーを設定する必要があります。
上の表だとusersテーブルの「id」が主キー(primary key)になり、postsテーブルの「user_id」が外部キー(foreign key)になります。
その点を踏まえて再び表を見てみると、postsテーブルのidが1と2の投稿は鈴木さんの投稿であり、idが3の投稿は佐藤さんの投稿であるということが分かります。
このように、どのユーザー(親)がどの投稿(子)をしたのかを区別するために設定するのが外部キーです。
そして、その際に使用するのがreferencesという型です。
※また、後で説明しますがreferences以外の型でも外部キーの設定は可能です。
referencesの使い方
先ほどの説明の時と同じように、
1人のuserが複数のpostを持っている(1対多)のアソシエーションを組む場合で説明します。
・migrationファイルの作成
% rails g model Post body:text user:references
・作成されたmigrationファイル
class CreatePosts < ActiveRecord::Migration[6.1]
def change
create_table :posts do |t|
t.text :body
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
カラム名を「user」としていますが、自動で「user_id」というカラム名に変更してくれます。
また、referencesを指定することで対象のモデルに対して、自動でアソシエーションが記述されます。
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
end
そしてreferencesの行にnull: falseとforeign_key: trueという記述があるのが分かります。
加えて、表示はされていませんがindexというオプションも自動で付与されています。
つまり、referencesを使用してmigrationファイル作成コマンドを実行したとき、以下の3つのオプションが自動で付与されます。
- null: false
- foreign_key: true
- index
以下では、それぞれのオプションについて1つずつ説明していきます!
null: falseとは?
・指定したカラムに対して空の状態で保存させることを禁止するオプションのこと
モデルファイルに記載するバリデーションであるpresence: trueも、同じようにカラムに対して空の状態で保存させることを禁止させる効果がありますが、
この記載だけだとRailsアプリ側からの保存を禁止するだけでSQLから実行を行うと保存ができてしまいます。
そのため、データベースレベルでの空保存を禁止したい場合は、migrationファイルにnull: falseを記述します。
foreign_key: trueとは?
・外部キー制約を設定するオプションのこと
外部キー制約を付けることで、存在しない主キーに紐づく外部キーは登録出来なくなります。
つまり、user.idが「1」であるデータが存在しなければ、posts.user_idが「1」であるデータは保存出来なくなります。
これは、データの整合性を保つために必要な処理ですが、設定しなければエラーになるということではありません。
また、子テーブル(posts)の外部キーにデータが保存されている親テーブル(users)のレコードは削除することが出来なくなります。
そのため、そのような親テーブルのレコードを削除したい場合は、関連する子テーブルのレコードを全て削除してからでないとエラーになってしまいます。
なので、親テーブルのレコードを削除する場合、子テーブルのレコードも削除する手間が発生してしまいます。
しかし、Railsではこのようにdependent: destroyを記述することで親テーブルのレコードを削除した際、同時に関連する子テーブルのレコードも削除してくれます。
# app/models/user.rb
has_many :posts, dependent: :destroy
そのため、foreign_key: trueを設定する際は、dependent: destroyを設定するようにしましょう。
indexとは?
・データを取得する際、1から順番に探しに行かずに、アルファベット順で検索してデータ取得を短縮するオプションのこと
indexを使用することでデータの取得は早くなるのですが、データの書き込みは通常よりも遅くなってしまいます。
またindexは、データ量が多い、かつ、それぞれのカラム名が異なるものに使用すると効果的です。
例えば、1万人のUserデータがあったとして、
nameカラム(ユーザーの名前)・sexカラム(ユーザーの性別)の2つのカラムがあるとします。
どちらのカラムもユーザーデータ数と同じ1万件ずつの数がありますが、
nameカラムの方は各ユーザー一意のものであるのに対し、sexカラムは「man」か「woman」の2択であるとします。
このような場合、nameカラムにindexを張ることは効果的ですが、sexカラムに張っても十分な効果は得られません。
integerを使用して外部キーを設定する場合
最後にカラムの型にintegerを使用した場合の、外部キー設定の方法について説明します。
・migrationファイルの作成
% rails g model Post body:text user_id:integer
・作成されたmigtationファイル
class CreatePosts < ActiveRecord::Migration[6.1]
def change
create_table :posts do |t|
t.text :body
t.integer :user_id
t.timestamps
end
end
end
referencesの時とは異なり、カラム名を「user_id」と記述しなければいけません。
また、対象のモデルに「belongs_to〜」の記述を自分で記述しなければいけません。
そして、以下の3つのオプションを付与したい場合は、migrationファイルに追加で記述する必要があります
- null: false
- foreign_key: true
- index
class CreatePosts < ActiveRecord::Migration[6.1]
def change
create_table :posts do |t|
t.text :body
t.integer :user_id, null: false #追加
t.timestamps
end
add_index :posts, :user_id #追加
add_foreign_key :posts, :users #追加
end
end
indexの場合はこちらの記述方法でもOKです
t.integer :user_id, index: true
まとめ
- 外部キーとは主キーの値を参照するためのカラムのこと
- referencesを使用することで「〜_id」や「belongs_to〜」を自動で作成してくれる
- referencesを使用することで「null: false」「foreign_key: true」「index」を自動で付与してくれる
- integerでも外部キーの作成は可能
参考記事
- migrateでカラムに外部キーを追加して、モデルを関連づける【Day 4/30 2nd】
- 【37】【Rails】インデックス、Rails学習者が陥るインデックスのジレンマ
- データベースにindexを張る方法
- Ruby on Railsの外部キー制約について
- migrationファイル内のnull: falseとは
- Railsの外部キー制約とreference型について