18

Rails 3 包括validates_associated在保存嵌套模型时自动调用的。该方法的问题是消息很糟糕 - “模型无效”

对于 Rails 2,有一些帖子针对此问题进行了攻击:

可能还有更多。最好有这些帖子中描述的更好的版本,它与 Rails 3 兼容。主要的改进是包括相关模型失败的原因。

4

4 回答 4

5

在关系上,您可以:autosave => true改用 which 将在您保存父模型时尝试保存子模型。这将自动运行孩子的验证,他们将报告正确的错误消息。

此外,如果您在子对象上添加一个必须设置父对象的存在验证,并且您通过关联构造子对象,那么您甚至不需要该autosave标志,并且您会收到一条漂亮的错误消息。例如:

class Trip < ActiveRecord::Base
  validates :name, :presence => true

  attr_accessible :name

  has_many :places, dependent: :destroy, :inverse_of => :trip
end

class Place < ActiveRecord::Base
  belongs_to :trip

  validates :name, :trip, presence: true

  attr_accessible :name
end

然后,您可以通过以下使用场景收到一条不错的错误消息:

> trip = Trip.new(name: "California")
=> #<Trip id: nil, name: "California"> 
> trip.places.build
=> #<Place id: nil, name: nil, trip_id: nil>
> trip.valid?
=> false
> trip.errors
=> #<ActiveModel::Errors:0x00000004d36518 @base=#<Trip id: nil, name: "California">, @messages={:places=>["is invalid"]}>
> trip.errors[:places]
=> ["is invalid"] 

我认为这validates_associated是自动保存儿童之前时代的遗物,不再是最好的做事方式。当然,这不一定有很好的记录。我不是 100% 肯定这也适用于 Rails 2.3,但我有一种感觉。这些更改是在添加嵌套属性功能时出现的(在 2.x 中的某个时候)。

这是我在 github 上发布的培训项目的简化代码片段。

于 2012-02-14T01:57:49.037 回答
3

我遇到了这个问题,最后我使用了Ben Lee给出的解决方案:

验证与模型的错误消息相关联

本 说:

您可以根据内置验证器的代码编写自己的自定义验证器。

查找 validates_associated 的源代码,我们看到它使用了“AssociatedValidator”。源代码是:

module ActiveRecord
  module Validations
    class AssociatedValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
        record.errors.add(attribute, :invalid, options.merge(:value => value))
      end
    end

    module ClassMethods
      def validates_associated(*attr_names)
        validates_with AssociatedValidator, _merge_attributes(attr_names)
      end
    end
  end
end

因此,您可以以此为例来创建一个自定义验证器,该验证器会像这样冒泡错误消息:

module ActiveRecord
  module Validations
    class AssociatedBubblingValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        (value.is_a?(Array) ? value : [value]).each do |v|
          unless v.valid?
            v.errors.full_messages.each do |msg|
              record.errors.add(attribute, msg, options.merge(:value => value))
            end
          end
        end
      end
    end

    module ClassMethods
      def validates_associated_bubbling(*attr_names)
        validates_with AssociatedBubblingValidator, _merge_attributes(attr_names)
      end
    end
  end
end

您可以将此代码放在初始化程序中,例如/initializers/associated_bubbling_validator.rb.

最后,您将像这样进行验证:

class User < ActiveRecord::Base
 validates_associated_bubbling :account
end

注意:上面的代码完全未经测试,但如果它不能完全工作,希望它足以让你走上正确的轨道

于 2012-11-27T21:44:07.390 回答
1

validates_associated 运行关联对象的类中指定的验证。父类级别的错误只是说“我的孩子无效”。如果您需要详细信息,请公开子对象上的错误(在视图中的子窗体级别)。

于 2011-03-03T04:28:23.537 回答
-1

大多数时候validates_existence_of就是我所需要的。

于 2011-01-26T10:07:53.023 回答