Rails

【Rails】1分で分かる!Not Null制約とpresence: trueの違いについて簡単にまとめてみた

Rails

 

今回は、テーブルのカラムに定義する「Not Null制約」とモデルのバリデーションに記述する「presence: true」の違いについて解説したいと思います。どちらもnullを許可しない記述だと認識されている方も多いとは思いますが、厳密には違いがあります。ぜひこの機会に理解を深めていただけたらと思います。

ビューに記載するrequired属性も似たような機能を持つね。

(結論)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
保存ができなかった場合のエラーメッセージをsave!メソッドを使用することで表示することができます。

 

一方で「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」
「required :true」を使用することで、空文字での送信を防ぐことができます。

 

クライアントサイドとサーバーサイドの両方で制約をかけることが理想です。

参考

Railsガイド バリデーション:
https://railsguides.jp/active_record_validations.html

「値あり」バリデーションの微妙な違い:
https://qiita.com/jkr_2255/items/e1ca683c6fbdb02c2ec3

 

 

今回は、テーブルのカラムに定義する「Not Null制約」とモデルのバリデーションに記述する「presence: true」の違いについて解説しました。この機会にしっかりと違いを理解した上で開発に取り掛かっていただけたらと思います。