更好的有关Rails 3的validates_associated方法?

时间:2010-09-08 13:05:58

标签: ruby-on-rails validates-associated

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

有一些帖子为Rails 2攻击了这个问题:

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

4 个答案:

答案 0 :(得分:5)

在关系上,您可以使用:autosave => true代替保存父级时尝试保存子模型的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

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

这是training project I posted on github的简化代码片段。

答案 1 :(得分:3)

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

validates associated with model's error message

Ben说:

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:-1)

大部分时间validates_existence_of都是我需要的。