在验证嵌套模型之前

时间:2016-02-21 00:54:22

标签: validation ruby-on-rails-4 nested-attributes

我有一个嵌套模型items,我试图将两列相加costquantity来设置最后一列price。 我需要在保存表单之前设置列price,我还需要验证模型。如果在保存之前未设置before_validationcostquantity回拨显然会中断。 关于如何将这些列相乘并验证模型的任何想法?

这是我的代码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

1 个答案:

答案 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_createbefore_update回调。或者,您可以选择设置价格(如果尚未设置):

self.price ||= cost * quantity.round(2)

这是你想要实现的目标吗?

更新:经过一些讨论(请参阅评论)后,处理此问题的最佳方式似乎是更新items回调中的所有子before_saveinvoice,因为每次保存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