【Rails】boolean型でもpresence: true にしたい!!
boolean型だとpresence: true にできない ><
Railsのモデルでバリデーションを設定する際に、 presence: true
を使ってカラムを必須にしたい時はよくありますよね。
そんな presence: true
ですが、boolean型のカラムを必須にしたい場合は、ちょっと工夫が必要なんです。
以下のようなモデルがあったとします。
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
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 でも引っかかってしまうようでした。
false.blank? # => true
Book.new(published: false).invalid? # => true
解決策
これらの問題を解決するには、inclusion
を使います。
inclusion
は、enum
のようにカラムに受け入れる値を制限することができます。
モデルを以下のように指定すると、 「published
というカラムは、true
と false
以外は受け付けない」という状態 になり、それ以外はバリデーションエラーとして弾きます。
これは実質、カラムを必須とさせる presence: true
と同じ役割を果たしているので、boolean型を必須にしたい時はinclusion
を使ってみてください。
class Book < ApplicationRecord
validates :published, inclusion: {in: [true, false]}
end
設計的にもメリット!?
このバリデーションの仕方だと、カラムにnilが入らなくなるので、エラーを防ぎやすくなります。(まぁ、null: falseを指定すれば良いだけではありますがmm)
DB上でもnullを防いで、Railsのバリデーション上でもnullを防げるので、一石二鳥?? ですね!! (強引w)
Discussion