2012-06-26 32 views
3

我有一个基本的发票设置与模型:发票,项目,LineItems。Rails 3:validates_presence_of验证错误的默认值和相关模型

# invoice.rb 
class Invoice < ActiveRecord::Base 
    has_many :line_items, :dependent => :destroy 
    validates_presence_of :status 

    before_save :default_values 

    def default_values 
    self.status = 'sent' unless self.status 
    end 
end 

# item.rb 
class Item < ActiveRecord::Base 
    has_many :line_items 
    validates_presence_of :name, :price 
end 

# line_item.rb 
class LineItem < ActiveRecord::Base 
    belongs_to :item 
    belongs_to :invoice 
    before_save :default_values 

    validates_presence_of :invoice_id 
    validates :item_id, :presence => true 
end 

模型中有更多,但我只提出上述为简单。

我收到以下错误:

2 errors prohibited this invoice from being saved: 
    Line items invoice can't be blank 
    Status can't be blank 

所以两个问题:

  1. 如果我删除validates :invoice_id, :presence => true我没有得到Line items invoice can't be blank错误信息了,但是为什么呢?我想验证line_items上的invoice_id,所有line_items都应该有一个invoice_id。我如何验证line_items上的invoice_id而不会出现错误?

  2. 为什么我得到Status can't be blank错误,如果我将它设置为默认值?我可以在invoices_controller上设置它,但我认为应该在模型中设置默认值,对吧?我如何验证状态的存在并在模型中仍然具有默认值?

回答

4

这些验证错误的两个正在发生,因为验证得到保存(和before_save回调之前)之前调用

我假设您使用nested_form来同时创建发票和订单项。如果是这种情况,您不希望validates :invoice_id, :presence => true上的订单项 - 发票和订单项同时进入,并且发票尚未保存,因此它没有ID 。如果您离开验证,则需要先创建并保存空的发票,然后再创建订单项,以便使用invoice_id。如果您只想确保在进行任何修改后仍然设置了invoice_id,则可以通过validates :invoice_id, :presence => true, :on => :update执行此操作,这会在创建订单项时(并且invoice_id尚不可用)跳过验证。

出于类似的原因,您遇到了问题validates :status, :presence => true - 通过请求传入的值正在验证,并且“状态”值不存在。验证后将运行before_save回调。您可以在before_validationafter_initialization回调中设置默认值,并且运行验证时这些值将在那里。

查看关于Rails的Callbacks文档以获取更多信息。

1

我将从2开始: 保存之前仅执行保存之前,意思是在对象通过验证并即将保存之后。如果验证失败 - 它将不会被执行。

至于1: 您可以举一个例子说明您如何创建发票?

0

问题1

尝试validates_associated检验这些关联模型都是有效的

问题2

最喜欢的答案说before_save得到验证后调用。你正在寻找的魔法是after_initialize,它会在对象的initialize方法被调用后运行。

class Invoice < ActiveRecord::Base 
    after_initialize :default_values 
    validates :status, presence: true 

private 

    def default_values 
    self.status ||= 'sent' 
    end 
end