为Rails 3更好的validates_associated方法?

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

Rails 2中有一些post攻击了这个问题:

  • http://rpheath.com/posts/412-a-better-validates-associated
  • http://pivotallabs.com/users/nick/blog/articles/359-alias-method-chain-validates-associated-informative-error-message

而且可能还有更多。 如果这些post中描述的Rails 3兼容,那将是一个很好的版本。 主要的改进是包括相关模型失败的原因。

在关系上,您可以使用:autosave => true而在保存父项时会尝试保存子模型。 这将自动运行子项的validation,并将报告正确的错误消息。

此外,如果您在子项上添加必须设置父项的状态validation,并通过关联构造子对象,您甚至不需要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.places.build => # > trip.valid? => false > trip.errors => #, @messages={:places=>["is invalid"]}> > trip.errors[:places] => ["is invalid"] 

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

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

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

validation与模型的错误消息相关联

本说:

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

查看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 

因此,您可以使用此示例来创建一个自定义validation程序,它会冒泡错误消息,如下所示:

 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

最后,你会这样validation:

 class User < ActiveRecord::Base validates_associated_bubbling :account end 

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

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

大多数时候, validates_existence_of都是我需要的。