我有一个嵌套模型items
,我试图将两列相加cost
和quantity
来设置最后一列price
。
我需要在保存表单之前设置列price
,我还需要验证模型。如果在保存之前未设置before_validation
和cost
,quantity
回拨显然会中断。
关于如何将这些列相乘并验证模型的任何想法?
这是我的代码item.rb
class Item < ActiveRecord::Base
belongs_to :invoice
validates :invoice_id, presence: true
validates :name, presence: true
validates_presence_of :quantity, :cost, :price
before_validation :set_price
def set_price
self.price = cost * quantity.round(2)
end
end
这是我的父模型invoice.rb
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items
accepts_nested_attributes_for :items, :reject_if => :all_blank, :allow_destroy => true
validates :sender, presence: true
validates_associated :items
before_save :increment_invoice_number, :set_amount
private
def increment_invoice_number
if published == true
self.invoice_number = user.invoices.where(:published => true).count + 1
end
end
def set_amount
self.amount = items.map(&:price).sum
end
端
答案 0 :(得分:3)
如果您始终要更新每次保存的价格,则无需验证其存在,只需将其从验证中删除即可添加before_save
回调,在验证后将被称为,因此您可以保证拥有有效的成本和数量字段。这里有一个指向Rails指南中这个地方的链接:
http://guides.rubyonrails.org/active_record_callbacks.html#available-callbacks
以下是一个例子:
class Item < ActiveRecord::Base
belongs_to :invoice
validates :invoice_id, presence: true
validates :name, presence: true
validates_presence_of :quantity, :cost
before_save :set_price
def set_price
self.price = cost * quantity.round(2)
end
end
如果您想在创建或更新时设置仅价格,则可以改为使用before_create
或before_update
回调。或者,您可以选择设置价格(如果尚未设置):
self.price ||= cost * quantity.round(2)
这是你想要实现的目标吗?
更新:经过一些讨论(请参阅评论)后,处理此问题的最佳方式似乎是更新items
回调中的所有子before_save
父invoice
,因为每次保存invoice
时都会调用它。下面是一个示例实现 - 我还从原始代码中删除了一些验证语句,并使用新语法组合了所有内容。此外,无论何时通过nested_attributes关联进行更新,都将验证items
。
class Item < ActiveRecord::Base
belongs_to :invoice
validates :invoice_id, :name, :quantity, :cost, presence: true
def set_price
self.price = cost * quantity.round(2)
end
end
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items
accepts_nested_attributes_for :items, :reject_if => :all_blank, :allow_destroy => true
validates :sender, presence: true
before_save :increment_invoice_number, :update_prices, :set_amount
private
def increment_invoice_number
if published == true
self.invoice_number = user.invoices.where(:published => true).count + 1
end
end
def update_prices
items.each(&:set_price)
end
def set_amount
self.amount = items.map(&:price).sum
end
end