在嵌套模型上验证之前
Before validation on nested model
我有一个嵌套模型 items
,我正在尝试将两列相乘 cost
和 quantity
以设置最后一列 price
。
我需要在保存表单之前设置列 price
,我还需要验证模型。如果在保存前未设置 cost
和 quantity
,则 before_validation
回调显然会中断。
关于如何将这些列相乘并验证模型的任何想法?
这是我的代码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
结束
如果您总是要在每次保存时更新价格,则不需要验证它的存在,因此只需将其从验证中删除并添加一个 before_save
回调,它将被调用在 验证之后,您将保证拥有有效的成本和数量字段。 link 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)
这是您要实现的目标吗?
更新: 经过一些讨论(见评论),处理这个问题的最佳方法似乎是更新 [=12] 中的所有子 items
=] 父 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
我有一个嵌套模型 items
,我正在尝试将两列相乘 cost
和 quantity
以设置最后一列 price
。
我需要在保存表单之前设置列 price
,我还需要验证模型。如果在保存前未设置 cost
和 quantity
,则 before_validation
回调显然会中断。
关于如何将这些列相乘并验证模型的任何想法?
这是我的代码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
结束
如果您总是要在每次保存时更新价格,则不需要验证它的存在,因此只需将其从验证中删除并添加一个 before_save
回调,它将被调用在 验证之后,您将保证拥有有效的成本和数量字段。 link 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)
这是您要实现的目标吗?
更新: 经过一些讨论(见评论),处理这个问题的最佳方法似乎是更新 [=12] 中的所有子 items
=] 父 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