【问题标题】:Duplicated Validation Across Form Objects and Models跨表单对象和模型的重复验证
【发布时间】:2014-12-20 09:23:49
【问题描述】:

在处理 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 的表单对象示例,每个都表明表单对象本身具有验证...没问题...部分好处包括能够在一个更具上下文意识的方式。

问题:

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

我如何验证动物的“只有一种棕色”规则没有:

  1. 重复太多。
  2. 大量代码使 Cage 仍然像 AR 模型一样工作

如果我们不复制验证码,当“只有一个棕色”为假的时候,Cage 就没有错误了……我们会提出,这需要控制器去捕捉和处理,也就是不好。

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

如果我们将验证代码从 Animal 完全移到 Cage 中,类似的问题:所有与 Animal 交互的对象都需要知道“只有一个棕色”规则,这只是复制验证器并开辟了一种容易忘记的方法在某处强制执行。

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

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

提前致谢!

【问题讨论】:

  • 您找到解决方案了吗?我正在使用 jquery 验证来对表单输入进行客户端验证,然后仍然只在 ActiveRecord 模型中进行验证,但是如果它们不允许在服务器端使用 js,我仍然想要一些东西。

标签: ruby-on-rails forms validation oop activerecord


【解决方案1】:

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

只是,请注意您的 attrs 的名称。

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

编辑:

如果您使用的是 Rails 3.2,则不能使用 ActiveModel::Model,但您会得到相同的结果:

class Cage
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  ... 

end

【讨论】:

  • 这就是问题的本质......它在两个地方都很重要。它对模型本身很重要,因为它需要在使用模型的任何地方强制执行,但它对表单对象也很重要,它需要像控制器/UI 的 AR 模型一样工作。在这两个地方都进行验证...需要以某种方式进行 SQL 查找或计算的验证...似乎令人不快,但不可避免。
  • 添加了我想说的更明显的代码示例。
  • 感谢 Alejandro,我已经查看了那条路线,但如果只有 brown 无法验证怎么办...animal.errors 现在包含 { :color =&gt; 'only one brown allowed' }。如果这被传递给 Cage,:color 不是笼子的属性,它是什么颜色...:bird_color...所以处理变得相当复杂,因为现在你需要知道动物在什么时候返回:color这意味着 :bird_color 表示 Cage 等,除非我们在两者之间映射列,否则表单不会在 :bird_color 上看到错误。
猜你喜欢
  • 2012-08-29
  • 2012-12-22
  • 2012-07-21
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多