ruby-on-rails - 跨表单对象和模型的重复验证

标签 ruby-on-rails forms validation oop activerecord

在处理 Form 对象和常规 Rails 模型时,基本验证器在哪里?

遵循 Rails 中将表单与持久层解耦的概念。我已经设置了一个表单对象 Cage一起创建两个对象...说AnimalPlant .

以下来自 http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ 的表单对象示例或 https://github.com/solnic/virtushttps://github.com/makandra/active_type ,每一个都表明 Form 对象本身有验证......没问题......部分好处包括能够以更加上下文感知的方式验证对象。

问题:

class Animal < ActiveRecord::Base

  validates :color, presence: true
  validate :only_one_brown

  private

  def only_one_brown
    if some_complex_thing
      errors.add(:color, 'can not have more than one brown animal.')
    end
  end
end

class Plant < ActiveRecord::Base
  validates :color, presence: true
end

class Cage
  include Virtus.model # or ActiveType or whatever
  include ActiveModel::Validations

  attribute :bird_color, String
  attribute :plant_color, String

  validates :bird_color, presence: true
  validates :plant_color, presence: true

  def save
    if valid?
      animal.save!
      plant.save!
      true
    else
      false
    end
  end

  def animal
    @animal ||= Animal.new(color: bird_color)
  end

  def plant
    @plant ||= Plant.new(color: plant_color)
  end
end

如何在没有以下情况下验证动物的“只有一个棕色”规则:
  • 太多的重复。
  • 大量代码使 Cage 仍然表现得像一个 AR 模型

  • 如果我们不复制验证码,当“only one brown”为 false 时,Cage 不会有错误...我们会提出,这需要 Controller 捕获和处理,这很糟糕。

    如果我们确实复制了代码,并且如果有多个自定义验证,我们将复制大量代码,并且处理 Animal 的每个其他表单对象现在都需要重复验证。

    如果我们将验证代码从 Animal 中完全移到 Cage 中,就会出现类似的问题:所有与 Animal 交互的对象都需要了解“只有一个棕色”规则,这只是复制验证器并打开了一种忘记执行它的简单方法某处。

    如果我们将 Animal 的错误数组向上移动到 Cage 的数组,则 Animal 的错误位于 :color ,这对 Cage 来说是模棱两可的,并且在客户端从未发送过的属性名称上显示错误。如果您想将 Animal 的错误键映射到 Cage 的错误键,现在您需要为每个 Form Object 保留一个映射,感觉很糟糕。

    有没有什么好的模式或方法来处理这种情况?当您开始使用表单对象时,我觉得这很常见,但所有示例都非常简单。

    提前致谢!

    最佳答案

    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ 上的第 3 点结束时作者说:“作为一个奖励,因为验证逻辑通常是上下文的,它可以在重要的地方准确定义,而不需要在 ActiveRecord 本身中保护验证。”我同意 Bryan Helmkamp 的观点,将验证放在重要的位置,您不需要复制它。

    编辑:

    如果我是你,我只会将验证放在 ActiveRecord 模型上。我将更新 Cage 类:

    def save
      if valid?
        ActiveRecord::Base.transaction do
          animal.save!
          plant.save!
        end
        true
      else
        false
      end
    rescue Exception => exception
      raise if valid?
      false
    end
    

    我将添加一个错误方法,该方法返回 Cage、Plant 和 Animal 实例的错误。

    编辑:

    我认为您可以重新定义有效吗?方法,然后错误工作正常:
    class Cage
      include ActiveModel::Model
    
      def valid_with_mymodels?
        valid_without_mymodels? && animal.valid? && plant.valid?
        animal.errors.each do |attribute, error|
          self.errors.add :"bird_#{attribute.to_s}", error
        end
        plant.errors.each do |attribute, error|
          self.errors.add :"plant_#{attribute.to_s}", error
        end
        errors.empty?
      end
      alias_method_chain :valid?, :mymodels
    
      ...
    
    end
    

    只是,小心你的属性名称。

    我不确定 Virtus 是如何工作的,在 Rails 4 中你可以使用 ActiveModel::Model,如果使用 Rails 3,我需要研究。

    编辑:

    如果您使用的是 Rails 3.2,则不能使用 ActiveModel::Model,但您会得到相同的结果:
    class Cage
      extend ActiveModel::Naming
      include ActiveModel::Conversion
      include ActiveModel::Validations
    
      ... 
    
    end
    

    关于ruby-on-rails - 跨表单对象和模型的重复验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26549612/

    相关文章:

    ruby-on-rails - Ubuntu + 使用 RVM 安装 Redmine 1.2.1、Ruby 1.8.7、Rails 2.3.11

    javascript - 使用 JavaScript 函数链接 html &lt;input&gt; 和 <output>

    jquery - 提交前如何验证表单

    javascript - 有人已经编写过 JavaScript 版本的 Zend Validators 了吗?

    ruby-on-rails - 为什么在Pundit中对待面向 `scope`的 Action (尤其是 `index` Action )有不同的对待?

    ruby-on-rails - 如何返回人类可读的时间范围?

    ruby-on-rails - Rails 3,在RAILS_ROOT上方显示来自本地文件系统的jpg图片

    javascript - Form To Wizard 和 Zurb Foundation 遵守验证

    ruby-on-rails - Rails 4 整数字段返回字符串

    javascript - 人们如何在 2013 年验证他们的网站?