カレーの恩返し

おいしいのでオススメ。

多対多の相手レコード数の最小を指定するバリデーション

こんなクラス構造

class Hoge < ApplicationRecord
  has_many :hoge_foos
  has_many :foos, through: :hoge_foos
  accepts_nested_attributes_for :hoge_foos
end
class Foo < ApplicationRecord
  has_many :hoge_foos
  has_many :hoges, through: :hoge_foos
end
class HogeFoo < ApplicationRecord
  belongs_to :hoge
  belongs_to :foo
end

 

やりたいこと

  • hogeのフォームからfooをチェックボックスで複数選択して紐付かせる
  • fooを少なくとも1つは選択してほしい

 

結論

これでいける

class Hoge < ApplicationRecord
    -- 略 --
    validate :validate_foos

  def validate_foos
    errors.add(:foos, 'を1つ以上選択してください') if foos.size.zero?
  end
end
  • validates を使ってできないものなのか
  • できるだけ命名したくない

探したらあった!…だけど

class Hoge < ApplicationRecord
    --- 略 ---
    validates :hoge_foos, 
        length: { 
            minimum: 1,
            message: 'を1つ以上選択してください' 
        }
end

http://stackoverflow.com/questions/4486749/rails-has-many-minimum-collection-size-update-validation

  • イケてる気がする!
  • エラーメッセージが「hoge_foosを1つ以上選択してください」
  • なんか違う

対応策を考える

i18nをいじってhoge_foosの表記を変える
ja:
    activerecord:
        attributes:
            hoge:
                hoge_foos: 'foos'

これはやったらダメな気がする


i18n%{attribute} を非表示にする
ja:
    errors:
    format: "%{message}"

全てのバリデーションのattributeが消えるため被害範囲が大きいのでダメ
Rails3のときはattributeを表示させなくするgemがあったらしい

 

まとめ

  • レールに乗っかるためにはカスタムバリデーションメソッドを作るのが一番良さそう