今回は、テーブルのカラムに定義する「Not Null制約」とモデルのバリデーションに記述する「presence: true」の違いについて解説したいと思います。どちらもnullを許可しない記述だと認識されている方も多いとは思いますが、厳密には違いがあります。ぜひこの機会に理解を深めていただけたらと思います。
(結論)Not Null制約とバリデーションの違い
「Not Null制約」とバリデーション「presence: true」の違いは結論、空文字(””)が許可されるかされないかの違いになります。「Not Null制約」では空文字は許可される一方で、バリデーションの「presence: true」では空文字は許可されません。
そのため空文字を含めた全てのnullを許可したくない場合は、「Not Null制約」とバリデーション「presence: ture」の両方を記載しておくと無難です。
↓ Not Null制約の記述例
class CreateItems < ActiveRecord::Migration[5.2]
def change
create_table :items do |t|
t.string :name, null:false
t.integer :price, null:false
t.text :detail, null:false
end
end
end
↓ バリデーションの記述例
class Item < ApplicationRecord
validates :name, :price, :detail, presence: true
end
(具体例)Not Null制約とバリデーションの違い
では「Not Null制約」と「presence: true」の違いについて具体的に見ていきましょう。まずは「Not Null制約」から見ていきます。
以下のようにitemsテーブルのnameカラムには「Not Null制約」を付与しましたが、Itemモデルにはバリデーションを記載していないと仮定します。
class CreateItems < ActiveRecord::Migration[5.2]
def change
create_table :items do |t|
t.string :name, null: false
t.timestamps
end
end
end
class Item < ApplicationRecord
end
この状態でnullと空文字をそれぞれ保存してみましょう。
ご覧の通り、nullは許可されていませんが空文字は許可されてしまっていますね。
% rails c
pry(main)> Item.new(name: nil).save!
(0.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
(0.3ms) BEGIN
Item Create (1.0ms) INSERT INTO `items` (`created_at`, `updated_at`) VALUES ('2020-09-30 03:19:26', '2020-09-30 03:19:26')
(0.3ms) ROLLBACK
ActiveRecord::NotNullViolation: Mysql2::Error: Field 'name' doesn't have a default value
pry(main)> Item.new(name: "").save!
(0.3ms) BEGIN
Item Create (0.4ms) INSERT INTO `items` (`name`, `created_at`, `updated_at`) VALUES ('', '2020-09-30 03:19:34', '2020-09-30 03:19:34')
(0.8ms) COMMIT
=> true
一方で「Not Null制約」を外し「presence: true」をモデルに付与した場合はどうなるのでしょうか。
class CreateItems < ActiveRecord::Migration[5.2]
def change
create_table :items do |t|
t.string :name
t.timestamps
end
end
end
class Item < ApplicationRecord
validates :name, presence: true
end
この状態で再度、nullと空文字をそれぞれ保存してみましょう。
ご覧の通り、今度はnullも空文字も許可されていませんね。
% rails c
pry(main)> Item.new(name: nil).save!
(0.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Nameを入力してください
pry(main)> Item.new(name: "").save!
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Nameを入力してください
このように「Not Null制約」とバリデーションの「presence: true」には空文字を許可するかしないかという違いがあるので覚えておきましょう。
結局、どう記述すればいいのか?
では結局のところ、nullも空文字も弾きたい場合はどのように記述すれば良いのでしょうか。結論、以下の3点を記述することが理想形となります。
- フォームに「required: true」
- モデルに「presence: true」
- テーブルに「null: false」
クライアントサイドとサーバーサイドの両方で制約をかけることが理想です。
参考
Railsガイド バリデーション:
https://railsguides.jp/active_record_validations.html
「値あり」バリデーションの微妙な違い:
https://qiita.com/jkr_2255/items/e1ca683c6fbdb02c2ec3
今回は、テーブルのカラムに定義する「Not Null制約」とモデルのバリデーションに記述する「presence: true」の違いについて解説しました。この機会にしっかりと違いを理解した上で開発に取り掛かっていただけたらと思います。