【问题标题】:Efficient way to report record validation warnings as well as errors?报告记录验证警告和错误的有效方法?
【发布时间】:2014-08-29 00:00:11
【问题描述】:

我有一个 Rails 项目,与大多数应用程序一样,我们有许多严格的验证规则,所有对象在持久化之前都必须遵守这些规则。当然,ActiveModel 的 Validations 非常适合这一点 - 我们使用 Rails 默认值和我们自己的手动验证的组合。

不过,我们越来越多地遇到一些用例,我们希望提醒用户注意以下情况:虽然他们的数据在最严格的意义上不是无效的,但有些元素他们应该审查,但本身不应阻止记录持久性的发生。几个例子,我的脑海中浮现:

  • 帖子标题已全部大写,可能有效,但可能无效
  • 一段正文比建议字数少或多的 x 字数多

验证模块是我们如何处理验证错误的一个很好的比喻——并且已经有很多匹配器可用——理想情况下我希望能够重用该基本代码,但生成warnings的集合errors 旁边的项目。这将使我们能够以不同的方式向用户突出这些案例,而不是暗示可能违反房屋风格的行为等同于更恶劣、更严格执行的规则。

我查看了诸如 activemodel-warnings 之类的 gem,但它们的工作原理是在验证记录时更改检查哪些匹配器,相应地扩展或缩小 errors 集合。同样,我查看了内置的 :on 参数进行验证,看看我是否可以手动滚动某些内容,但所有违规行为最终都会出现在错误集合中,而不是被分离出来。

有人试过类似的吗?我无法想象我是唯一一个想实现这个目标的人,但现在我正在画一个空白......

【问题讨论】:

  • 你看过这个问题的接受答案了吗? stackoverflow.com/questions/3342449/… 看来您可以创建一个单独的ActiveModel::Errors 并将其用作警告的存储
  • 这绝对很有趣,本杰明,谢谢。这种方法确实意味着我们必须为警告编写单独的匹配器,而不是那些产生错误的匹配器,即使它们的内部结构是相同的。尽管存在代码重复的可能性,但我可能会接受这一点。一种重新调整用途的方法,例如,Rails 的内置长度验证匹配器能够根据调用上下文输出警告而不是错误......但如果它能让我到达我需要的地方,我准备好务实成为!
  • 我没有深入研究它,但是看起来您将不得不复制所有这些验证类。快速浏览一下 Rails 源确认:record.errors.add(attribute, ...) 你需要record.warnings.add(attribute, ...)
  • 另外,它现在可能已经 3 岁了,但也许你可以在这个宝石中找到一些灵感github.com/stevehodgkiss/validation-scopes
  • 是的,您必须重写所有匹配器可能是对的。我一直在玩弄将我的基类包装在装饰器中的可能性,使用它自己的错误对象来捕获一些验证,然后将它们作为单独的警告对象公开——但这似乎只是铲除了巨大的痛苦,因为一种避免一些潜在重复的方法……

标签: ruby-on-rails validation activemodel


【解决方案1】:

另一种选择是为警告设置一个单独的对象:

class MyModelWarnings < SimpleDelegator
  include ActiveModel::Validations

  validates :name, presence: true

  def initialize(model)
    super
    validate
  end  

  def warnings; errors; end
end

class MyModel < ActiveRecord::Base
  def warnings
    @warnings ||= MyModelWarnings.new(self).warnings
  end
end

【讨论】:

    【解决方案2】:

    几年后,但在较新的 Rails 版本中,有一种更简单的方法:

      attr_accessor :save_despite_warnings
    
      def warnings
        @warnings ||= ActiveModel::Errors.new(self)
      end
    
      before_save :check_for_warnings
      def check_for_warnings
        warnings.add(:notes, :too_long, count: 120) if notes.to_s.length > 120
    
        !!save_despite_warnings
      end
    

    那你可以这样做:record.warnings.full_messages

    【讨论】:

      【解决方案3】:

      这是我为 Rails 3 项目编写的一些代码,它完全符合您在此处所说的内容。

      # Define a "warnings" validation bucket on ActiveRecord objects.
      #
      # @example
      #
      #   class MyObject < ActiveRecord::Base
      #     warning do |vehicle_asset|
      #       unless vehicle_asset.description == 'bob'
      #         vehicle_asset.warnings.add(:description, "should be 'bob'")
      #       end
      #     end
      #   end
      #
      # THEN:
      #
      #   my_object = MyObject.new
      #   my_object.description = 'Fred'
      #   my_object.sensible? # => false
      #   my_object.warnings.full_messages # => ["Description should be 'bob'"]
      module Warnings
        module Validations
          extend ActiveSupport::Concern
          include ActiveSupport::Callbacks
      
          included do
            define_callbacks :warning
          end
      
          module ClassMethods
            def warning(*args, &block)
              options = args.extract_options!
              if options.key?(:on)
                options = options.dup
                options[:if] = Array.wrap(options[:if])
                options[:if] << "validation_context == :#{options[:on]}"
              end
              args << options
              set_callback(:warning, *args, &block)
            end
          end
      
          # Similar to ActiveModel::Validations#valid? but for warnings
          def sensible?
            warnings.clear
            run_callbacks :warning
            warnings.empty?
          end
      
          # Similar to ActiveModel::Validations#errors but returns a warnings collection
          def warnings
            @warnings ||= ActiveModel::Errors.new(self)
          end
      
        end
      end
      
      ActiveRecord::Base.send(:include, Warnings::Validations)
      

      顶部的 cmets 显示了如何使用它。您可以将此代码放入初始化程序中,然后所有 ActiveRecord 对象都应该可以使用警告。然后基本上只需在每个可能有警告的模型顶部添加一个warnings do 块,然后手动添加任意数量的警告。在您在模型上调用 .sensible? 之前,不会执行此块。

      另外,请注意,由于警告不是验证错误,因此模型在技术上仍然有效,即使它不是“明智的”(正如我所说的那样)。

      【讨论】:

      • 谢谢!正是我想要的!也适用于 Rails 4。我已经将它与 Rails 错误验证一起使用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-10
      • 2013-05-12
      • 1970-01-01
      相关资源
      最近更新 更多