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

Ruby on rails Rails model.valid?刷新自定义错误并错误返回true,ruby-on-rails,activerecord,Ruby On Rails,Activerecord,我试图将自定义错误添加到我的用户模型的实例中,但是当我调用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")

我试图将自定义错误添加到我的用户模型的实例中,但是当我调用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={}>

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

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

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

在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\u singleton\u方法
可以覆盖
.valid?
行为。

这不是使用提供的验证/框架的替代方法。但是,在某些特殊情况下,您希望优雅地返回errd模型。只有在其他选择不可能的情况下,我才会使用此选项。我不得不使用这种方法的少数场景之一是在服务对象内部创建一个模型,其中创建的某些部分失败(如解析依赖实体)。我们的域模型负责这种类型的验证是没有意义的,因此我们不将其存储在那里(这就是为什么服务对象首先要进行创建)。然而,为了简化API设计,可以方便地挂起一个域错误,如“关联实体foo not found”,并通过正常的rails 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
用作某个实例.extend(ModelWithErrors.new(:name,“is gibberish”;:height,“is胡言乱语”)创建新的关注点

app/models/concerns/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

如果您的问题是关于如何绕过这种行为,请检查
错误
数组的大小,而不是使用
有效?
方法。遗憾的是,这不是一个可行的选择。
有效?
将错误返回到表单,但是如果我使用
错误。计数
检查,它不会传递错误。非常奇怪的行为。希望,您描述的行为是100%逻辑,
.valid?
重新处理所有内容,这很好。我不认为当出现明显错误消息时,为u.valid?返回true是合乎逻辑的。此外,新约定是
验证:状态,包含:{in:%w{yes no}
一篇关于验证上下文的好文章:在这种情况下,
方法会改变它所调用的对象的状态吗?什么是“上下文”?在我的例子中,它是
attr\u accessor
,它告诉模型
foo\u validation
。但这不是一个好主意,我建议您使用表单对象进行上下文验证。我很确定,仅仅链接到图书的营销页面是违反堆栈溢出的一些规则的。这本书甚至没有具体说明正在讨论的主题。这绝对是肮脏的!:P6年后!迟做总比不做好:P我最近遇到过类似的问题,但选择完全分离验证对象(有时是直接形式的对象)来管理不同的上下文。更干净,我不需要重新定义方法。
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"]}