我有2个型号:
Invoice has_many :lines
Line belongs_to :invoice
我希望确保给定Line
的{{1}}的总和与相关Invoice
的总和相匹配。
我试过这个:
Invoice
但是
我也看过一个关于AssociatedValidator的问题,但我还是没能理解如何使用它:(
答案 0 :(得分:2)
目前尚不清楚您想要验证的内容,因为您的示例与之前描述的内容不同。
我认为这样的事情应该有效,使用before_add callback:
class Invoice < AR::Base
has_many :lines, :before_add => :validate_total
def validate_total(invoice, line)
totals = invoice.lines.sum(:line_value)
if totals + line.line_value > invoice.total
invoice.errors.add(:total, " should be lower or equal to the total amount of the invoice")
return false # I think you can alternatively raise an exception here
end
...
答案 1 :(得分:1)
我可能会将其解释错误,但如果total
是invoices
表中的列,我建议将其删除。相反,将它作为一种方法,让方法加起来Line
价格加上任何调整。否则,您在数据库中有重复。这样你无论如何都不需要验证任何东西:)
更一般地说,在ActiveRecord
中添加关联模型的验证效果不佳。在某些情况下,几乎不可能,在另一些情况下 - 很难做到正确。我想你已经看到它容易出错了。我建议避免它并尝试设计您的数据库,以便您不需要(在这种情况下使用Invoice#total
作为方法)。
答案 2 :(得分:0)
花了一些时间才找到使用accepts_nested_attributes_for
出现的问题。但是答案只是说很难,即使不是不可能!。 accepts_nested_attributes_for
是一种有点复杂的方法,但是它可以工作-除非您试图基于子模型的计算来验证模型。我可能已经找到一种克服计算问题的方法。
我正在研究基于Web的双向记帐应用程序,该应用程序具有以下基本模型;
class Account < ApplicationRecord
has_many :splits
has_many :entries, through: :splits
end
class Entry < ApplicationRecord
has_many :splits, -> {order(:account_id)}, dependent: :destroy, inverse_of: :entry
validate :balanced?
end
class Split < ApplicationRecord
belongs_to :entry, inverse_of: :splits
belongs_to :account
validates_associated :account
validates_associated :entry
end
条目(交易)必须至少有两个拆分,拆分中的金额属性(或借方/贷方)之和必须等于0。尽管validate :balanced?
会照顾好它,但显然JavaScript错误允许输入不平衡。我尚未找到该错误,但是由于条目不平衡,所以我无法更新它,因为valid?
在我尝试添加的新拆分中不起作用(返回false)。
分类帐accepts_nested_attributes_for
表单已退出一些Javascript,该Javascript不应允许提交不平衡的事务。均衡?没有在创建时设置错误,但是在更新时存在错误。我修复它的方法不是使用无效的验证,而是依靠与@entry.update(entry_params)
一起调用的方法:
class Entry < ApplicationRecord
has_many :splits, -> {order(:account_id)}, dependent: :destroy, inverse_of: :entry
# validate :balanced? # took this out since its after the fact, balanced? method can still be called
accepts_nested_attributes_for :splits,
:reject_if => proc { |att| att[:amount].to_i.zero? && att['account_id'].to_i.zero?},
allow_destroy: true
def valid_params?(params)
split_sum = 0
params_hash = params.to_h
params_hash[:splits_attributes].each{|k,s| split_sum += s[:amount].to_i if s[:_destroy].to_i.zero?}
unless split_sum.zero?
errors.add(:amount, "Unbalanced: debits, credits must balance")
return false
else
return true
end
end
end
end
# update action from Entry Controller
def update
respond_to do |format|
if @entry.valid_params?(entry_params) && @entry.update(entry_params)
format.html { redirect_to account_path(session[:current_acct]), notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: @entry }
else
# ... errr
end
end
end
同样,这无非是验证参数,而不是在这种情况下不起作用的模型验证。
这可能与答案2大致相同,但不使用回调,而只是在控制器中调用