Railsのバリデーションは、:on
オプションをつけることで任意のコンテキストのときだけ実行させることができます。
validates :name, presence: true # 常にバリデーションする
validates :description, presence: true, on: :hoge # context: :hogeのときだけバリデーションする
しかし、context: :hoge
以外のときだけバリデーションしたいときはどうすればいいのでしょうか。:on
で:hoge
以外のコンテキストを全て指定するなどというのは流石にありえませんが、Railsガイドには説明がありません。
そのようにしたい場合は、以下のようにvalidation_context
を参照したlambdaやProcを:unless
に渡して条件を指定すれば実現できます。
validates :name, presence: true # 常にバリデーションする
validates :description, presence: true, unless: -> { validation_context == :hoge } # context: :hoge以外のときだけバリデーションする
なぜこのような書き方になるのでしょうか。
まず:unless
で指定できるのは、以下のように:on
を:if
に読み替えており:on
と:if
は同等、:unless
は::if
の逆なので:unless
は:on
の逆となるためです。
# https://github.com/rails/rails/blob/5-2-stable/activemodel/lib/active_model/validations.rb#L154
def validate(*args, &block)
options = args.extract_options!
if args.all? { |arg| arg.is_a?(Symbol) }
options.each_key do |k|
unless VALID_OPTIONS_FOR_VALIDATE.include?(k)
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?")
end
end
end
if options.key?(:on)
options = options.dup
options[:on] = Array(options[:on])
options[:if] = Array(options[:if])
options[:if].unshift ->(o) {
!(options[:on] & Array(o.validation_context)).empty?
}
end
set_callback(:validate, *args, options, &block)
end
またvalidation_context
を参照できるのは、save
する際に呼ばれる以下の処理において、引数から渡ってきた:context
をself.validation_context
にセットしているためです。
# https://github.com/rails/rails/blob/5-2-stable/activemodel/lib/active_model/validations.rb#L336
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
errors.clear
run_validations!
ensure
self.validation_context = current_context
end
Railsでこの時だけ特定のバリデーションを検証したくないときしたこと
Rails のバリデーションを特定のコンテキスト「以外」で実行させる
なお条件付きバリデーションをwith_optionsでグループ化する記法がありますが、ここでも同様に、以下のようにvalidation_context
を参照して特定のコンテキスト以外の条件をグループ化することができます。
with_options unless -> { validation_context == :hoge } |not_hoge| # context: :hoge以外のときだけバリデーションする
not_hoge.validates :name, presence: true
not_hoge.validates :description, presence: true
end