【问题标题】:validates associated with model's error message验证与模型的错误消息相关联
【发布时间】:2011-11-15 07:00:49
【问题描述】:

我有两个模型如下

class User < ActiveRecord::Base
 validates_associated :account
end

class Account < ActiveRecord::Base

  belongs_to :user

  #----------------------------------Validations--Start-------------------------
  validates_length_of :unique_url, :within => 2..30 ,:message => "Should be atleast 3 characters long!"
  validates_uniqueness_of :unique_url ,:message => "Already Taken"
  validates_format_of :unique_url,:with => /^([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])$/ , :message => " Cannot contain special charaters"
  #----------------------------------Validations--End---------------------------
end

现在,当我将帐户与用户关联时,它会显示

“帐号无效”

相反,我想直接从该模型获取错误消息。 所以应该说

"Should be atleast 3 characters long!""Already Taken"" Cannot contain special charaters"

有没有办法做到这一点?

我不想给出一个笼统的信息,比如:

validates_associated :account , :message=>"one of the three validations failed"

【问题讨论】:

  • 您应该能够访问帐户验证消息,就像访问用户的验证消息一样——它们只是在帐户上。
  • 我不想那样做......这是我的问题背后的想法
  • 所以将错误信息从账户复制给用户。
  • @Gaurav,你看到我发布的解决方案了吗?这似乎完成了你正在寻找的东西。无法使用内置的“validates_associated”使错误消息冒泡(如果您检查源代码,您会发现它没有任何选项)。但正如我的帖子所示,制作这个验证器的错误冒泡版本很容易。
  • 不,那里没有副本。您会将帐户的消息复制给用户;错误存储在地图中,可以任意操作。基本上是本答案的手动版本。顺便说一句,他的回答正是你所要求的——当你想要一些不是三个框架提供的东西时,你必须自己去做。他的方法对应用程序开发人员隐藏了它,这似乎是您所要求的。

标签: ruby-on-rails validation


【解决方案1】:

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

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

module ActiveRecord
  module Validations
    class AssociatedValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
          record.errors.add(attribute, :invalid, options.merge(:value => value))
        end
      end
    end

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

因此,您可以以此为例创建一个自定义验证器,该验证器会像这样冒泡错误消息(例如,将此代码添加到 config/initializers/associated_bubbling_validator.rb 中的初始化程序):

module ActiveRecord
  module Validations
    class AssociatedBubblingValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        ((value.kind_of?(Enumerable) || value.kind_of?(ActiveRecord::Relation)) ? 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

所以你现在可以像这样验证:

class User < ActiveRecord::Base
 validates_associated_bubbling :account
end

另外,请务必在您的 has_many 关联中添加 validate: false,否则,Rails 将默认验证关联,您最终会收到两条错误消息,一条由您的新 AssociatedBubblingValidator 给出,一条由泛型给出通过 Rails。

【讨论】:

  • 老实说,我认为这实际上是最优雅和最红宝石的方式。不确定您在寻找什么“香草”——这个问题没有完全直接的解决方案。
  • 我终于放弃了......你的答案似乎最合适......如果我找到更好的答案会回来......目前不满意。
  • 在我的情况下,除非 v.valid?在 validates_each 方法行中给出了错误,例如“未定义的方法‘有效?’对于#<:associations::collectionproxy::activerecord_associations_collectionproxy_check:>”。但最后我把那条线改成了除非v.nil?然后下一行 v.errors.full_message 到 record.errors.full_message 然后它对我有用。但奇怪的问题是所有错误消息都出现了两次(但默认的“无效”消息从列表中消失)。然后我明白一条消息来自模型,例如存在 => true,而另一条消息来自客户验证器。所以你需要阻止 1
  • stackoverflow.com/a/31916721/3192470 这是一个基于此答案的带有固定代码的答案。
  • @MadhanAyyasamy 我编辑了答案以使其保持最新状态,因此它不会在 has_many 关联上抛出任何异常(以前是这样)。
【解决方案2】:

也许你可以试试下面给出的东西

validates_associated :account , :message=> lambda{|class_obj, obj| obj[:value].errors.full_messages.join(",") }

通过 obj[:value] ,您正在访问经过验证的帐户对象。 obj[:value].errors 也会给你错误。

【讨论】:

  • 不错。在 stabby 语法中,这是 validates_associated :account, message: -&gt;(_class_obj, obj){ obj[:value].errors.full_messages.join(',') }
  • 这应该是公认的答案,当它可以在 1 或 2 行代码中完成时,无需创建自定义验证器。
  • 使用:message 参数意味着关联名称仍然附加在错误消息之前。
【解决方案3】:

一个普通的解决方案是为关联对象呈现第二个错误:

<%= render :partial => 'shared/errors', :locals => {:instance => @account.owner} %>
<%= render :partial => 'shared/errors', :locals => {:instance => @account} %>

【讨论】:

    【解决方案4】:

    如果仍然需要,以下适用于 Rails 5.2

    #app/validators/with_own_error_messages_validator.rb
    class WithOwnErrorMessagesValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        Array(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
    
    #app/models/project.rb
    class Project < ApplicationRecord
      validates :attachments, with_own_error_messages: true
      has_many :attachments, as: :attachable, validate: false
    end
    

    注意 has_many 关联上的 validate: false。这是必要的,因为 Rails 现在默认验证关联。没有它,您会收到Attachment 错误消息以及通用Attachments is invalid

    希望这会有所帮助。

    【讨论】:

      【解决方案5】:

      要获取帐户的错误消息,您必须在该实例上调用 errors 方法:

      @user.account.errors
      

      @account = @user.build_account
      @account.errors
      

      或在视图中:

      <%= error_messages_for :account %>
      

      我假设这是一个 has_one 关系。

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题...我想验证关联的对象,并让 valid? 方法返回 false,但不添加(多余的)消息,如“帐户无效”。我只想在帐户字段旁边显示帐户验证消息。

        我发现完成此操作的唯一方法是覆盖run_validations!,如下所示。在本例中,关联为has_many :contacts

        def run_validations!
          valid = super
          contacts.each do |contact|
            valid = false unless contact.valid?(validation_context)
          end
          valid
        end
        

        不要使用valid &amp;&amp;= contacts.all? ...,因为如果基类已经无效,这将阻止对联系人的验证,并且如果其中一个联系人验证失败,则会停止迭代。

        也许将其推广到某个关注模块中是一个想法,但我只需要它一次。

        【讨论】:

          【解决方案7】:

          对于仍在使用 Rails 2 的用户,您可以使用以下代码覆盖 validates_associated:

          module ActiveRecord::Validations::ClassMethods
            def validates_associated(association, options = {})
              class_eval do
                validates_each(association) do |record, associate_name, value|
                  associate = record.send(associate_name)
                  if associate && !associate.valid?
                    associate.errors.each do |key, value|
                      record.errors.add(key, value)
                    end
                  end
                end
              end
            end
          end
          

          来源:http://pivotallabs.com/alias-method-chain-validates-associated-informative-error-message/

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-12-09
            • 1970-01-01
            • 1970-01-01
            • 2015-10-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多