💭
【Rails】booleanカラムにpresence trueバリデーションをつけて保存時にエラーが発生した
少しハマってしまったのでメモとして残しておく。
結論
boolean
型のカラムにバリデーションを設定したいときはこうする。
- validates :published, presence: true
+ validates :published, inclusion: { in: [true, false] }
詳細
以下のようなBlogモデルがあるとする。
class Blog < ApplicationRecord
validates :title, presence: true
validates :body, presence: true
validates :published, presence: true #ここに注目
end
published
カラムの型は真偽値となっている。
スキーマはこちら。
class CreateBlogs < ActiveRecord::Migration[7.1]
def change
create_table :blogs do |t|
t.string :title, null: false
t.text :body, null: false
t.boolean :published, default: false, null: false
t.timestamps
end
end
end
blog
インスタンスを作る。
> blog = Blog.new(title: 'asdf', body: 'adsf', published: false)
=> #<Blog:0x000000012b938718 id: nil, title: "asdf", body: "adsf", published: false, created_at: nil, updated_at: nil>
valid?
で評価するとfalse
が返る。
> blog.valid?
=> false
save!
をするとActiveRecord::RecordInvalid
が発生して保存できない。
> blog.save!
(irb):6:in `<main>': Validation failed: Published can't be blank (ActiveRecord::RecordInvalid)
一見すると、false
というbooleanを代入しているからblank
ではないはずと思える。
presence: true
バリデーションの実装を見ると、今回の場合だとif value.blank?
でtrue
が返ってくることからrecord.errors.add
が実行されている様子。つまりエラーになる。
boolean
型カラムにバリデーションをかけたい場合、inclusion
を使う方法がある。
Active Record バリデーション - Railsガイド
validates :published, inclusion: { in: [true, false] }
# inのエイリアスとしてwithinがある
validates :published, inclusion: { within: [true, false] }
こうすることでインスタンスがvalid
にもなるし、無事レコードも保存できる。
> blog = Blog.new(title: 'asdf', body: 'adsf', published: false)
=> #<Blog:0x000000012a1907f0 id: nil, title: "asdf", body: "adsf", published: false, created_at: nil, updated_at: nil>
> blog.valid?
=> true
> blog.save!
TRANSACTION (0.1ms) begin transaction
Blog Create (3.9ms) INSERT INTO "blogs" ("title", "body", "published", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) RETURNING "id" [["title", "asdf"], ["body", "adsf"], ["published", 0], ["created_at", "2024-06-21 08:23:35.542415"], ["updated_at", "2024-06-21 08:23:35.542415"]]
TRANSACTION (0.4ms) commit transaction
=> true
boolean
型カラムにはたいていの場合デフォルト値を設定すると思う。もしデフォルト値がfalse
だったら、presence :true
バリデーションを書くと保存されなくなるというお話でした。
Discussion