Rails model.valid?刷新自定义错误并错误地返回 true



我正在尝试向我的用户模型的实例添加自定义错误,但是当我调用valid时? 它正在擦除自定义错误并返回 true。

[99] pry(main)> u.email = "test@test.com"
"test@test.com"
[100] pry(main)> u.status = 1
1
[101] pry(main)> u.valid?
true
[102] pry(main)> u.errors.add(:status, "must be YES or NO")
[
    [0] "must be YES or NO"
]
[103] pry(main)> u.errors
#<ActiveModel::Errors:[...]@messages={:status=>["must be YES or NO"]}>
[104] pry(main)> u.valid?
true
[105] pry(main)> u.errors
#<ActiveModel::Errors:[...]@messages={}>

如果我在模型中使用 validate 方法,那么它可以工作,但这个特定的验证是从不同的方法中添加的(这需要传递参数):

User
def do_something_with(arg1, arg2)
  errors.add(:field, "etc") if arg1 != arg2
end

由于上述原因,即使将错误添加到实例中,user.valid? 也会返回 true。

在 ActiveModel 中,valid?定义如下:

def valid?(context = nil)
  current_context, self.validation_context = validation_context, context
  errors.clear
  run_validations!
ensure
  self.validation_context = current_context
end

因此,现有的错误被清除是意料之中的。您必须将所有自定义验证放入一些validate回调中。喜欢这个:

validate :check_status
def check_status
  errors.add(:status, "must be YES or NO") unless ['YES', 'NO'].include?(status)
end

如果你想强制你的模型显示错误,你可以做一些像这样肮脏的事情:

your_object = YourModel.new 
your_object.add(:your_field, "your message")
your_object.define_singleton_method(:valid?) { false }
# later on...
your_object.valid?
# => false
your_object.errors
# => {:your_field =>["your message"]} 

define_singleton_method方法可以重写.valid?行为。

这不是使用提供的验证/框架的替代品。但是,在某些特殊情况下,您希望正常返回 errd 模型。我只会在其他替代方案不可能的情况下使用它。我不得不使用这种方法的少数方案之一是在服务对象内部创建一个模型,其中创建的某些部分失败(如解析依赖实体)。我们的领域模型负责这种类型的验证是没有意义的,所以我们不将其存储在那里(这就是服务对象首先进行创建的原因)。但是,为了API设计的简单性,可以方便地挂起域错误,例如"找不到关联的实体foo",并通过正常的轨道422/不可处理的实体流返回。

class ModelWithErrors
  def self.new(*errors)
    Module.new do
      define_method(:valid?) { false }
      define_method(:invalid?) { true }
      define_method(:errors) do
        errors.each_slice(2).with_object(ActiveModel::Errors.new(self)) do |(name, message), errs|
          errs.add(name, message)
        end
      end
    end
  end
end

用作some_instance.extend(ModelWithErrors.new(:name, "is gibberish", :height, "is nonsense")

产生新的关注点

app/models/concern/static_error.rb

module StaticError
  extend ActiveSupport::Concern
  included do
    validate :check_static_errors
  end
  def add_static_error(*args)
    @static_errors = [] if @static_errors.nil?
    @static_errors << args
    true
  end
  def clear_static_error
    @static_errors = nil
  end
  private
  def check_static_errors
    @static_errors&.each do |error|
      errors.add(*error)
    end
  end
end

包括模型

class Model < ApplicationRecord
  include StaticError
end
model = Model.new
model.add_static_error(:base, "STATIC ERROR")
model.valid? #=> false
model.errors.messages #=> {:base=>["STATIC ERROR"]}

满足需求的一种简洁方法是上下文,但如果您想快速修复,请执行以下操作:

#in your model
attr_accessor :with_foo_validation
validate :foo_validation, if: :with_foo_validation
def foo_validation
  #code 
end
#where you need it
your_object.with_foo_validation = true
your_object.valid?

最新更新