rails以复杂的形式验证多个子模型

时间:2009-08-17 13:33:12

标签: ruby-on-rails validation children nested-forms associations

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

基本上一个人有很多分布。分布具有(除其他外)百分比属性。对于给定的人,他们的分布必须总共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业务很丑陋,但这或多或少有效(有时一些待处理的发行版在表单中找不到,当它发现错误并重新渲染时)。有一个额外的细微差别让我觉得这是一个糟糕的方法:如果我的distirbutions关联是用帮助方法定义的,如下所示,我不能在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的过程中将params本身作为文本处理。但这只是错误的。 :)

其他人对整个儿童系列进行验证?什么是正确的方法呢?

1 个答案:

答案 0 :(得分: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