Rails validates_associated和错误消息

时间:2016-09-09 05:30:10

标签: ruby-on-rails validation

我想显示来自Child模型的验证消息,它给了我基本的Child无效。我尝试了所有解决方案但都没有成功。 简而言之,这些模型是:

Class Parent
  ...
  has_many :children, dependent: :destroy, inverse_of: :parent
  accepts_nested_attributes_for :children, allow_destroy: true
end

Class Child
  belongs_to :parent
  validate :something
  def something
    check = # Here I check something
    if check < 5
      errors[:base] << "validation on children failed"
    end
  end
end

如果我将validates_associated :children添加到Parent模型,那么我实际上会收到两条Children is invalid条消息,这有点奇怪。

无论如何,我可以将此块添加到Parent模型中,并获取我想要添加到Parent:base错误列表的任何验证消息:

validate do |parent|
  parent.children.each do |child|
    check = # Here I check something
    if check < 5
      errors[:base] << "validation on children failed"
    end
  end
end

但是,我仍然会在Child模型的基础上得到验证错误消息,所以现在我们会有3条错误消息:“Children is invalidChildren is childrenvalidation on children failed”......丑陋。

由于我将块添加到Parent模型,我可以从Child模型中删除验证,从Parent中删除validates_associated :children,然后保存实例将不会通过验证,但属于儿童模型没有验证,将保存/更新。

希望这清楚地解释。如果你能提供解决方案,那就太棒了!

更新1:关于accepts_nested_attributes_for

记录非常糟糕。正如我解释的那样,我在没有请求的情况下对我的nsted模型进行了验证。我相信原因是accepts_nested_attributes_for实际上从嵌套模型运行验证。 这意味着无论验证类型如何,我从嵌套模型获得的所有验证消息都会给我一条消息。我可能会设法以某种方式调整该消息的输出,但它仍然是单个消息,并不是特定于嵌套模型遇到的问题。

话虽如此,accepts_nested_attributes_for确实允许reject_if参数。这对我没有帮助,因为我需要在嵌套模型上为多个条目运行唯一性验证(一个父母的许多子项都需要具有唯一名称)。

要点:

我认为我需要在子模型中运行验证,但是找到一种方法,通过accepts_nested_attributes_for,使用特定的消息向Parent模型报告。这本质上就是我在寻找的东西! 我想我需要编写一个自定义验证器。有什么想法吗?

4 个答案:

答案 0 :(得分:2)

添加我的解决方案,因为我希望它对浏览此问题的人有帮助:

class Parent < AR
  has_many :children, inverse_of: :parent
  validates_associated :children, message: proc { |_p, meta| children_message(meta) }

  def self.children_message(meta)
    meta[:value][0].errors.full_messages[0]
  end
  private_class_method :children_message
end

class Child < AR
  belongs_to :parent, inverse_of: :children
  validates :name, presence: true
end

答案 1 :(得分:0)

建议的解决方案

这么简单的事情不应该那么烦人。我仍然没有找到任何优雅的解决方案,但我没有更多的时间浪费在这上面。 为了公平对待他人,我会发布我的(丑陋的)解决方案。注意,这个解决方案仍然需要工作。

我最终创建了一个从两个模型中收集错误的方法,而不是让Rails这样做。

def collect_errors
  errors = []
  if self.errors.any?
    parent_errors = self.errors.full_messages
    parent_errors.delete("YOURCHILDMODELNAME is invalid")
    errors << parent_errors.join(", ")
  end
  self.children.each do |child|
    if child.errors.any?
       errors << child.errors.messages.values.join(", ")
    end
  end
   return errors.join(", ")
end

然后我在每个模型上进行所有验证,在视图中我只显示收集的错误,而不是父模型错误:

flash[:warning] = "Failed to create Parent: #{@parent.collect_errors}"

答案 2 :(得分:0)

也可以组合错误消息:

@parent_model.children.each do |child_record|
  child_record.errors.messages.each do |field, messages|
    messages.each do |msg|
      @parent_model.errors.add("#{child_record.id} #{field}", msg)
    end
  end
end

答案 3 :(得分:0)

ActiveModel Errors类现在具有副本!并合并!为此目的的方法。复制将替换父邮件,而合并则同时保留:父邮件和子邮件,使用合并或复制将从父邮件中删除邮件

您按照以下说明在父模型中编写它,并使用forms_errors_for:

  validate do |parent|
    errors.messages.clear 
    parent.children.each do |children|
      next if children.valid?
      
      errors.merge! children.errors
    end
  end

ActiveModel错误的定义


 # Merges the errors from <tt>other</tt>.
    #
    # other - The ActiveModel::Errors instance.
    #
    # Examples
    #
    #   person.errors.merge!(other)

    def merge!(other)
      @messages.merge!(other.messages) { |_, ary1, ary2| ary1 + ary2 }
      @details.merge!(other.details) { |_, ary1, ary2| ary1 + ary2 }
    end