2011-05-12 122 views
21

我正在寫軌道嚮導的形式;例如一個模型對象的多個輸入頁面。如何根據父對象的狀態驗證嵌套模型對象?

我的方法的基礎是Ryan Bates的Multistep form railscast:http://railscasts.com/episodes/217-multistep-forms(如果有人想知道下面的一些代碼背後的原因)。

受到審查的對象這裏是「參與者」,其中有一個「地址」

我的問題是,我只是想驗證嵌套對象(地址),當用戶試圖過去將地址輸入進步屏幕。

class Participant < ActiveRecord::Base 
    has_one :address 
    accepts_nested_attributes_for :address 
    validates_presence_of :first_name, :last_name, :if => self.current_step == "name" 
    ... 
    def steps = %w[ name address confirm ] # the steps the wizard will follow 
end 

和地址:這是通過所謂的「CURRENT_STEP」

所以我有一個參與者對參與者模型的屬性目前跟蹤

class Address < ActiveRecord::Base 
    belongs_to :participant 
    validates_presence_of :address1, :state, :suburb, :postcode #, :if => participant.current_step == "address" 
end 

這種方法的原理是在嚮導的每個步驟的控制器(未顯示)上調用「創建」操作,並且它僅在處理每個步驟時驗證模型的子集。

當前,當我完成第一個屏幕(「名稱」)並嘗試進入地址步驟時,地址驗證將被觸發,並且我將返回到帶有驗證錯誤的「名稱」屏幕以顯示空白地址細節。

所以我在這裏嘗試了很多方法,其中最後一部分是上面顯示的地址驗證的註釋掉條件 - 我發現這不起作用,因爲我只是構建參與者 - >地址對象,但不保存它們。因此@participant.address得到我的地址對象,但@participant.address.participant爲空,因爲地址還沒有一個participant_id外鍵來查找它的父項。

我掙扎的原因似乎是包含了超級方便的accepts_nested_attributes_for方法。我期待使用validates_associated進行驗證,但我發現accepts_nested_attributes_for標籤都很好地傳播了表單參數以創建嵌套的模型對象,但也確保在所有情況下方法調用participant#valid?方法進行地址驗證。

所以我的困境是如何最好地使用participant#valid?方法來驗證部分完整的模型,基於參與者中的current_step參數?

編輯 - 更新,刪除了額外的信息,並提煉到核心問題

+0

只是一個猜測,但你試過 驗證:address,:if => Proc.new {|參與者| participant.current_step =='name'} – dogenpunk 2011-05-16 19:43:50

+0

我嘗試過的第一件事情之一 - 但如上所述(可能不太清楚,我承認)地址對象沒有參與者,直到它被保存,所以我得到一個錯誤,調用current_step on無班級。 – Phantomwhale 2011-05-16 23:32:50

+0

添加:inverse_of到你的關係,所以地址對象將建立時參考其參與者。 – graywh 2013-03-30 03:35:56

回答

18

添加一個虛擬屬性您Address型號:

class Address < ActiveRecord::Base 
    belongs_to :participant 
    attr_accessor :skip_validation 
    validates_presence_of :address1, :state, :suburb, :postcode, 
          :unless => :skip_validation 
end 

當設置了current_step時,在地址對象上設置虛擬屬性。

class Participant < ActiveRecord::Base 
    has_one :address 
    accepts_nested_attributes_for :address 
    attr_accessor :current_step 
    validates_presence_of :first_name, :last_name, 
         :if => lambda {|r| r.current_step == "name"} 


    def current_step=(value) 
    unless (value == "address") 
     address.skip_validation = true 
    end  
    @current_step = value 
    end  
end 
+1

好主意。我調用屬性'skip_validation'並使用':unless'而不是':if',所以默認是驗證。迄今爲止效果很好。 – Thilo 2012-01-24 10:56:31

+0

好吧,幾個月前我就移除了我的導軌嚮導,並且自那時以來一直在處理更多的嵌套模型問題,但回過頭來看看問題的確切答案是非常好的。謝謝。 – Phantomwhale 2012-01-24 22:57:23

+0

這是金子!謝謝! – 2013-03-11 18:13:44

0

如何在你的控制器,你打電話之前participant.valid?只是分配給它:

@participant.address.participant = @participant 
0

雖然ActiveRecord的「驗證」方法是非常方便,有什麼能阻止你「滾你自己」,或者使用before_save掛鉤的。您可以將validates_presence_of驗證程序替換爲Address,其中before_save掛鉤僅在某些情況下進行驗證。然後accepts_nested_attributes_for想必不會看到它。