Rails 3 有更好的 validates_linked 方法吗?

发布于 2024-09-17 20:28:34 字数 617 浏览 1 评论 0原文

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

有一些帖子攻击 Rails 2 的此问题:

可能还有更多。如果有一个像这些帖子中所描述的兼容 Rails 3 的更好版本,那就太好了。主要改进是包括关联模型失败的原因

Rails 3 includes the validates_associated which is automatically called when saving a nested model. The problem with the method is the message is terrible - "Model(s) is invalid"

There have been a few posts attacking this issue for Rails 2:

and there are probably more. It would be great to have a better version as described in these posts that is Rails 3 compatible. The main improvement would be to include why the associated model fails.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

怕倦 2024-09-24 20:28:34

在关系上,可以使用 :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_linked 是自动保存子项之前时代的遗物,并且不再是最好的处理方式。当然,这不一定有很好的记录。我不能 100% 确定这也适用于 Rails 2.3,但我有一种感觉。这些更改是在添加嵌套属性功能时发生的(2.x 中的某个时候)。

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

On the relationship, you can use :autosave => true instead which will try to save children models when you save the parent. This will automatically run the validations of the children and they will report with proper error messages.

Moreover, if you add a presence validation on the child that the parent must be set, and you construct the child objects through the association, you don't even need the autosave flag, and you get a beautiful error message. For example:

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

Then you can get an nice error message with the following usage scenario:

> 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"] 

I think validates_associated is a relic of the era before autosaving of children and isn't the best way to do things any more. Of course that's not necessarily documented well. I'm not 100% sure that this also applies to Rails 2.3, but I have a feeling it does. These changes came when the nested attributes feature was added (which was sometime in 2.x).

This is a simplified snippet of code from a training project I posted on github.

暖阳 2024-09-24 20:28:34

我遇到了这个问题,最后我使用了 Ben Lee

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

Ben 说:

您可以编写自己的自定义验证器,基于内置验证器的代码。

查找 validates_linked 的源代码,我们看到它使用“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/linked_bubbling_validator.rb

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

class User < ActiveRecord::Base
 validates_associated_bubbling :account
end

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

I was having this problem, and in the end I used the solution given here by Ben Lee:

validates associated with model's error message

Ben says:

You can write your own custom validator, based on the code for the built-in validator.

Looking up the source code for validates_associated, we see that it uses the "AssociatedValidator". The source code for that is:

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

So you can use this as an example to create a custom validator that bubbles error messages like this:

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

You can put this code in an initializer, something like /initializers/associated_bubbling_validator.rb.

Finally, you'd validate like so:

class User < ActiveRecord::Base
 validates_associated_bubbling :account
end

NOTE: the above code is completely untested, but if it doesn't work outright, it is hopefully enough to put you on the right track

薄荷港 2024-09-24 20:28:34

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

validates_associated runs the validations specified in the associated object's class. Errors at the parent class level simply say 'my child is invalid'. If you want the details, expose the errors on the child object (at the level of the child's form in the view).

笔芯 2024-09-24 20:28:34

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

Most of the time validates_existence_of is all I need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文