2

我正在以复杂的形式使用多个子模型(例如http://railsforum.com/viewtopic.php?id=28447)。表单效果很好,但在将表单数据接收到数据库之前,我需要验证子模型集的属性。我想出了一种主要工作,非常笨拙的方式来做到这一点。似乎必须有更好的方法,所以我正在寻求建议......

基本上一个人有很多分布。分布具有(除其他外)百分比属性。对于给定的人,他们的分配必须总计 100% 才有效。这对我来说是“交易”,但我认为我应该先给验证者一个机会。

我尝试将其编写为自定义验证器,但验证器仅适用于已保存到数据库中的数据。它没有检查表单提交的参数。换句话说,我能够通过表单输入无效的百分比,这些百分比被保存,然后由于模型中已经存在错误数据,随后的编辑都失败了。

接下来我在我的 Person 模型中扩展了 update_attributes,添加了一个事务:

def update_attributes(params)
  retval = true
  self.transaction do
    retval = super

    unless distributions_exactly_100?
      retval = false
      errors.add_to_base("Distribution must add up to exactly 100%")
      raise ActiveRecord::Rollback
    end
  end
  retval
end

retval 业务很丑陋,但这或多或少是有效的(有时,当它发现错误并重新渲染时,表单中会缺少一些待处理的分发版)。还有一个细微的差别让我相信这是一个糟糕的方法:如果我的分布关联是用辅助方法定义的,如下所示,我不能在我的update_attributes()(或在distributions_exactly_100?)中使用辅助方法,因为它们进入数据库而不是在刚刚分配但尚未提交的分布集上运行。

 has_many :distributions do
   def for_month_and_year(month, year)
     find :all, :conditions => ['month = ? and year = ?', month, year]
   end

   def total_for_month_and_year(month, year)
     sum :percentage, :conditions => ['month = ? and year = ?', month, year]
   end

   ...

   def years_and_months
     ds = find(:all, :order => 'year DESC, month DESC')
     (ds.collect {|d| [d.year, d.month]}).uniq
   end

 end

我能想到的唯一另一件事是在进入 update_attributes 的过程中将参数本身作为文本处理。但那是错误的。:)

还有其他人对整个儿童集合进行验证吗?正确的方法是什么?

4

1 回答 1

2

我不建议在update_attributes. 将验证保留在正常位置。

回到您的问题,您能否更改验证检查以处理内存中的分布而不是在数据库上执行计算?

# in Person model
validate :ensure_distributions_equal_100

def ensure_distributions_equal_100
  percent = distributions.map(&:percent).sum
  if percent != 100
    errors.add_to_base("Distribution must add up to exactly 100%, they are #{percent}")
  end
end
于 2009-08-18T14:48:11.547 回答