0
Help us understand the problem. What are the problem?

posted at

updated at

Railsでcheckboxとvalidationを使っただけなのに

結論

  • form.check_boxはfalse値を送信できない。
  • Ruby on Railsでmodelの真偽値(true/false)のvalidationを書くときは、presence: trueではなくinclusion: [true, false]を使わないといけない。

経緯

Rails をはじめように沿ってrailsを触ってみていました。ただ、そのままやるのはおもしろくないなと思ったため、ガイドでは記事(Article)を表示するウェブページを作成したところを、少しだけアレンジしてTodoを表示するものとして作成していました。TodoArticleのもつ値に加えてhasDoneという完了済みか否かを表す値を持ちます。

hasDoneTodoを作成するときに、View側からは

  <div>
    <%= form.label :hasDone %><br>
    <%= form.check_box :hasDone, {checked: false}, true, false %>
  </div>

form.check_boxを使って指定するような形にしました。

また、バリデーションも有効化してありました。

class Todo < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true
  validates :hasDone, presence: true
end

現象

check_boxをチェックすると問題なくレコードが作成されるのですが、check_boxをチェックしていないと、@todo.saveを実行時保存に失敗するようになってしまいました。

問題点

1つ目は、form.check_boxがfalseの値を送信しないことです。これはRailsのcheck_boxのチェック時、アンチェック時にどんな値がサーバに送られるかが詳しいのでこちらを参照ください。

2つ目は、真偽値のバリデーションにpresence:を使ってはいけないことです。Railsガイドのpresence公式ドキュメントの該当箇所には、このケースの取り扱い方法が載っています。

presence: trueを指定すると、Railsは内部でfieldが存在するかどうかを.blank?で調査しています。しかし、false.blank?は必ずfalseを返すため、真偽値のバリデーションにpresenceを使ってはならず、かわりにinclusion: [true, false]を使うようにとのことでした。

解決方法

本当はform.check_boxになんとかしてfalse値を送ってもらいたかったのですが、難しそうだったので以下のようにController側で処理することにしました。

def create
    # もとのオブジェクトは変更できない
    p = todo_params
    # フォームの入力がfalseの場合、keyなしのparamsが渡されるため
    # validation前にfalseを埋め込む
    if !p.respond_to? :hasDone
        p[:hasDone] = false
    end
    @todo = Todo.new(p)

    if @todo.save
        redirect_to @todo
    else
        render :new, status: :unprocessable_entity
    end
end

private
  def todo_params
    params.require(:todo).permit(:title, :body, :hasDone)
  end

また、model側もドキュメントの通りに変更を加えました。

class Todo < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true
  validates :hasDone, inclusion: [true, false]
end

この状態でフォームから送信したところ、問題なくレコードが作成されました。

まとめ

  • check_boxはfalse値を送信しない。なにかしらのハンドリングや、真偽値の送信に別の方法(0/1など)を使わないといけなさそう。
  • modelのvalidationは、それぞれの型にあったものを選ぶ。真偽値の場合はinclusion: [true, false]

今回はなにげなく2つ知らなかったことを踏んでいたので、解決にだいぶ時間がかかりました。この記事が私と同じように「ちょっと試してみただけなのに」で大ハマリした人を抜け出せさせられたらなと思います。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?