4

我在 Rails 模型中有自定义属性设置器,我在其中添加了验证错误。然而,当记录属性被更新时,“真”返回结果,这让我有点困惑。任何提示如何在自定义设置器中使用验证错误?

模型:

class Post < ActiveRecord::Base
  attr_accessible :body, :hidden_attribute, :title

  def hidden_attribute=(value)
    self.errors.add(:base, "not accepted")
    self.errors.add(:hidden_attribute, "not_accepted")
    write_attribute :hidden_attribute, value unless errors.any?
  end
end

控制台输出:

1.9.3p194 :024 > Post.last
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
 => #<Post id: 1, title: "asdsaD", body: "la", hidden_attribute: nil, created_at: "2013-11-13 16:55:44", updated_at: "2013-11-13 16:56:06">
1.9.3p194 :025 > Post.last.update_attribute :hidden_attribute, "ka"
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
   (0.0ms)  begin transaction
   (0.0ms)  commit transaction
 => true

我为此案例制作了一个示例应用程序。

4

3 回答 3

8

好的,我理解了问题的核心。不可能做我想做的事情,因为一旦验证过程开始,所有验证错误都会被清除。

https://github.com/rails/rails/blob/75b985e4e8b3319a4640a8d566d2f3eedce7918e/activemodel/lib/active_model/validations.rb#L178

自定义二传手踢得太早了:(

于 2013-11-15T08:31:33.813 回答
5

在您的设置器中,您可以将错误消息存储在临时 hash中。然后您可以创建一个 ActiveRecord 验证方法来检查此临时哈希是否为空,并将错误消息复制到errors.

例如,

def age=(age)
  raise ArgumentError unless age.is_a? Integer
  self.age = age
rescue ArgumentError
  @setter_errors ||= {}
  @setter_errors[:age] ||= []
  @setter_errors[:age] << 'invalid input'
end

这是 ActiveRecord 验证

validate :validate_no_setter_errors

def validate_no_setter_errors
  @setter_errors.each do |attribute, messages|
    messages.each do |message|
      errors.add(attribute, message)
    end
  end
  @setter_errors.empty?
end

要查看此操作:

[2] pry(main)> p.age = 'old'
=> "old"
[3] pry(main)> p.save!
   (1.0ms)  BEGIN
   (1.2ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Age invalid input
[4] pry(main)> p.errors.details
=> {:age=>[{:error=>"invalid input"}]}
于 2018-09-18T12:22:44.803 回答
2

这应该有效。注意从 update_attribute 到 update_attributes 的变化。update_attribute 跳过验证。

Post.last.update_attributes(:hidden_attribute => "ka")

def hidden_attribute=(value)
  self.errors.add(:base, "not accepted")
  self.errors.add(:hidden_attribute, "not_accepted")
  write_attribute :hidden_attribute, value #removed the condition here because save doesn't do anything when the object is not changed
end

当你不写属性时,对象没有变化,save什么都不做,只返回true。

于 2013-11-13T19:04:49.427 回答