Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
31
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@shimbaco

Railsにて、複数あるチェックボックスが1つも選択されていなければエラーになるバリデーションを作る

環境

  • Ruby 1.9.2p320
  • Ruby on Rails 3.2.3

要件

例えばウェブサイトに以下のような入力フォームがあるとします。

  • あなたの好みを教えてください。(必須項目)
    • ゾンビ
    • 幽霊
    • ナイアーラトテップ

この3つの選択肢はチェックボックスで複数選択できるようにします。また、1つも選択されなかったときはエラーになるようにします。

方法

以下のようなテーブルを定義します。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.boolean :like_ghost
      t.boolean :like_nyarlathotep
      t.boolean :like_zombie
      t.timestamps
    end
  end
end

次に以下のようなビューを用意します。

p あなたの好みを教えてください。(必須項目)
= form_for(@user) do |f|
  = hidden_field_tag 'user[like]'
  = f.check_box :like_zombie
  | ゾンビ
  = f.check_box :like_ghost
  | 幽霊
  = f.check_box :like_nyarlathotep
  | ナイアーラトテップ
  = f.submit '保存'

バリデーションをするために hidden_field_tag メソッドを使用して like というフィールドを用意しています。

次に以下のようなモデルを用意します。

# coding: utf-8

class User < ActiveRecord::Base
  attr_accessor :like
  attr_accessible :like, :like_ghost, :like_nyarlathotep, :like_zombie

  validates :like, present_like: true
end

like はデータベースのフィールドとして存在していないため、attr_accessor メソッドで読み書きできるようにする必要があります。そして、like_* が1つも選択されなかったときにバリデーションエラーにするため、 present_like という独自のバリデーションを作成します。

# coding: utf-8

class PresentLikeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    like_name = ['ghost', 'nyarlathotep', 'zombie']
    like_values = like_name.map { |name| record.__send__("like_#{name}") }

    unless like_values.include?(true)
      record.errors[attribute] << (options[:message] || 'を選択してください。')
    end
  end
end

validate_each メソッドに渡される3つの引数にはそれぞれ以下のようなデータが渡されます。

  • record: #<User id: nil, like_ghost: false, like_nyarlathotep: false, like_zombie: false, created_at: nil, updated_at: nil>
  • attribute: :like
  • value: ""

like_name.map { |name| record.__send__("like_#{name}") } でレコードに入っている各 like_* の値が入った配列を生成し、include? メソッドを使用して1つでも true が入っているか (チェックボックスが選択されているか) どうかを判別しています。

record.errors[attribute] << (options[:message] || 'を選択してください。')options には、モデル内で

validates :like, present_like: { message: 'を選択してね' }

などとしたときに、{ message: 'を選択してね' } が格納されます。ちなみに前の例では :present_liketrue を指定していましたが、 false などを指定しても問題なく動作します。

この独自バリデーションのコードを present_like_validator.rb という名前で保存します。_validator をファイル名の最後に付けないといけないようです。

以上です。もっと良い方法があったら教えてもらえると嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
31
Help us understand the problem. What are the problem?