🛤️

【Rails】boolean型でもpresence: true にしたい!!

2024/01/04に公開

boolean型だとpresence: true にできない ><

Railsのモデルでバリデーションを設定する際に、 presence: true を使ってカラムを必須にしたい時はよくありますよね。

そんな presence: true ですが、boolean型のカラムを必須にしたい場合は、ちょっと工夫が必要なんです。

以下のようなモデルがあったとします。

db/migrate/create_books_migration.rb
create_table "books", charset: "utf8mb4", collation: "utf8mb4_bin", force: :cascade do |t|
  t.string "name", null: false
  t.boolean "published", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end
app/model/book.rb
class Book < ApplicationRecord
  validates :published, presence: true
end

この時、上のモデルのようにboolean型にpresence: true とバリデーションを設定すると、

false をカラムに入れた場合、バリデーションエラーになってしまいます ><

実現したいことは boolean型のカラムを必須にしたい = trueでもfalseでもない場合(nilの場合)はバリデーションとして弾くこと です。

以下、このような挙動になる原因を見ていきましょう。

原因・理由

Railsガイドによると

このヘルパーは、指定された属性が空(empty)でないことを確認します。値がnilや空文字でない、つまり空でもなければホワイトスペースでもないことを確認するために、内部でObject#blank?メソッドを使っています。

とあり、 presence: true は内部でblank?メソッドが実行されていることが原因 のようです。

以下のように、false は blank(空) の扱いなので、presence: true でも引っかかってしまうようでした。

console
false.blank? # => true

Book.new(published: false).invalid? # => true

解決策

これらの問題を解決するには、inclusionを使います。

inclusionは、enumのようにカラムに受け入れる値を制限することができます。

モデルを以下のように指定すると、 publishedというカラムは、truefalse 以外は受け付けない」という状態 になり、それ以外はバリデーションエラーとして弾きます。

これは実質、カラムを必須とさせる presence: true と同じ役割を果たしているので、boolean型を必須にしたい時はinclusionを使ってみてください。

app/model/book.rb
class Book < ApplicationRecord
  validates :published, inclusion: {in: [true, false]}
end

設計的にもメリット!?

このバリデーションの仕方だと、カラムにnilが入らなくなるので、エラーを防ぎやすくなります。(まぁ、null: falseを指定すれば良いだけではありますがmm)

DB上でもnullを防いで、Railsのバリデーション上でもnullを防げるので、一石二鳥?? ですね!! (強引w)

Discussion