2012-06-26 35 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