Techioz Blog

Railsのafter_validationでエラーを削除する方法

概要

モデルの一部の値が検証に失敗した場合に null に設定できる機能が必要です。私のモデルは次のように設定しています。

class MyModel < ApplicationRecord
  attr_accessor :should_set_fields_to_null_if_invalid

  NULL_IF_INVALID_FIELDS = [:address, :phone]

  after_validation :set_fields_to_null_if_invalid, if: :should_set_fields_to_null_if_invalid
  ...
  # custom validations for address and phone
  ...

  def set_fields_to_null_if_invalid
    NULL_IF_INVALID_FIELDS.each do |attribute|
      self[attribute] = nil if errors.key?(attribute)
      errors.delete(attribute)
    end
  end
end

基本的に、エラーが存在する場合は削除し、属性を null に設定していますが、次のエラーが発生します。

     ActiveRecord::RecordInvalid:
       Validation failed: 
     # /Users/albertjankowski/.rvm/gems/ruby-3.0.3/gems/activerecord-6.1.7.3/lib/active_record/validations.rb:80:in `raise_validation_error'
     # /Users/albertjankowski/.rvm/gems/ruby-3.0.3/gems/activerecord-6.1.7.3/lib/active_record/validations.rb:53:in `save!'

検証メッセージが表示されずにまだ失敗する理由がわかりません。これを実装することについて提案がある人はいますか?

解決策

save でレコードを保存します。これにより例外が発生します…そのため、after_validation メソッドは実行されません。 save! の代わりに save メソッドを使用することも、例外をレスキューして属性値を修正することもできます。

begin
  @my_model_instance.save!
rescue
  # fix the problematic attributes and save again
end

ただし、検証は通常、入力内の何かを修正する必要があることをユーザーに通知するために使用されます。より良い方法は、次のように、ActiveRecord の検証スキームを使用せずに、単に住所と電話の属性をテストし、テストに失敗した場合に修正することです。

class MyModelController < ApplicationController
  def create
    # read csv file
    @my_model = MyModel.new(...params from csv file...)
    @my_model.address = nil unless @my_model.valid_imported_address?
    @my_model.phone = nil unless @my_model.valid_imported_phone?
    @my_model.save!
  end
end

class MyModel < ApplicationRecord
  def valid_imported_address?
    # some test for compliant address, return true if acceptable
  end

  def valid_imported_phone?
    # some test for compliant phone, return true if acceptable
  end
end