Rails模型。有效吗? 刷新自定义错误并错误地返回true

我试图将自定义错误添加到我的用户模型的实例,但是当我调用有效时? 它正在擦除自定义错误并返回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 #["must be YES or NO"]}> [104] pry(main)> u.valid? true [105] pry(main)> u.errors # 

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

 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 

因此预期存在的错误被清除。 您必须将所有自定义validation放入一些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? 行为。

这不是使用提供的validation/框架的替代品。 但是,在某些特殊情况下,您希望优雅地返回errd模型。 我只会在其他替代方案无法实现的时候使用它。 我必须使用此方法的少数场景之一是在服务对象内部创建模型,其中创建的某些部分失败(如解析依赖实体)。 我们的域模型负责这种类型的validation是没有意义的,所以我们不将它存储在那里(这就是服务对象首先进行创建的原因)。 然而,为了简化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")

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

 #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?