2012-06-10 68 views
2

我正在努力讓自己的頭腦對這個問題感興趣,所以我希望我能夠很好地解釋它。我有一個嵌套模型的表單。一個簡化版本的東西如下(使用Rails 3.0.13);在Rails更新驗證失敗後未被破壞的嵌套屬性

#parent.rb 

class Parent < ActiveRecord::Base 
    has_many :children, :dependent => destroy 
    accepts_nested_attributes_for :children, :allow_destroy => true 
    validates_presence_of :thing 
    validate :children_unique 

    def children_unique 
    errors[:base] << "You have the same child listed more than once!" if self.children.map{|x| [child.name, child.age]} != self.children.map{|x| [child.name, child.age]}.uniq 
    end 
end 


#child.rb 

class Child < ActiveRecord::Base 
    belongs_to :parents 
end 

#parents_controller.rb 

class ParentsController < ApplicationController 
    load_and_authorize_resource :parent #Using cancan for authorization. This calls @parent = Parent.find(params[:id]); authorize! :update, @parent; at the start of the update method 

    def update 
    if @parent.update_attributes(params[:operation]) 
     redirect_to @parent.admission, :notice => 'Operation was updated successfully' 
    else 
     flash.now[:warning] = "Parent was NOT updated!" 
     render :action => "edit" 
    end 
    end 
end 

到目前爲止都是非常標準的。我的表單也以相當標準的方式建立。我打電話給parent_form.fields_for :children,並在fields_for塊中爲子女提供部分內容。每個子部分表單包含一個刪除鏈接,並且當它被點擊時,javascript用於設置一個隱藏字段,因此_destroy的屬性設置爲「1」,並且該部分從視圖中隱藏。

這在大多數情況下效果很好,但我發現的奇怪問題如下;

如果我正在編輯一個已經有2個孩子的現有父母,並刪除其中的1個孩子,然後將'thing'設置爲空,則表單無法按預期驗證(因爲'thing'不存在)並且編輯視圖被重新渲染。在結果看來,我刪除的孩子再次出現!其隱藏的_destroy字段設置爲「true」,如果我再次填寫「thing」並提交表單,則更新的父級只有1個子級。

我通過向嵌套的子div div <div class='fields'<%= " style='display: none;'" if f.object._destroy %>>添加條件樣式標記來處理此問題,因此如果在嘗試更新記錄之前將其刪除,它將不再顯示。這實現了它的目標,但是,如果我這樣做並提交表單而不糾正空的'thing'字段,以便模型再次驗證失敗,並且在出現的下一個編輯表單中添加一個與之前刪除的子項相同的新子項並填寫'thing'字段,即使第一個相同的子項的_delete屬性設置爲「true」,模型現在也會失敗children_unique驗證。

很明顯,我已經把自己綁在這裏,我已經嘗試了大量的替代方法和修改,這是我迄今爲止最好的。這是非常非常接近的,但我仍然有這種奇怪的邊緣情況下,實際上可能永遠不會發生,但這表明我不太瞭解我的控制器和模型的交互方式。

有人可以設置我嗎?

回答

1

你應該讓刪除鏈接成爲一個ajax調用控制器,該控制器立即刪除孩子並從頁面中刪除它的標記,這樣,即使驗證確實失敗,您將已經刪除它,並沒有這兩個問題將會發生。

+0

這是一個好主意 - 我真的希望不要下井AJAX途徑在這個應用程式所有,但它會解決這個問題。 – brad

+0

您可以在更新操作開始時在控制器中使用AJAX。 – user2340939

1

爲了理解發生了什麼,我們首先需要看看Rails如何處理數據庫交互。也就是說,Rails將事務中的所有數據庫交互包裝在一起,這樣如果某件事失敗了,一切都會恢復。這就是爲什麼當出現驗證錯誤時,您會在日誌中看到「ROLLBACK」。它試圖進行修改,但出現了錯誤,所以它將其回滾。沒有傷害完成。

同樣,當您在同一表單中操作父記錄和子記錄時,它們都在事務中處理。這實際上是一件好事。如果某件事失敗了,你不需要做一些改變,一件事會失敗。

只要您將_destroy設置爲true,您只需將該子記錄標記爲銷燬即可。但是,這種破壞不會立即發生。相反,孩子會一直等到父母被保存(save返回true)。在你的情況下,這不會發生:由於驗證錯誤,致電save失敗。但是,這是一件好事:如果父記錄未能保存,應該沒有理由刪除子記錄。

這就是說,我的建議是將這些子記錄包裝在div中,添加一個數據元素,如data-destroy,它被設置爲屬性_destroy。無論何時刪除鏈接被點擊,將其設置爲true,並且每當頁面被加載時,確保所有設置爲true的div被隱藏。

希望這有助於你理解發生了什麼事情!

+0

這絕對是一個有用的解釋,並符合我的想法。儘管如此,我並不完全遵循你所建議的數據銷燬元素。過去如何確保孩子不被處理? – brad

+0

數據銷燬元素的使用僅僅是隱藏包含標記爲破壞的兒童的適當div的手段。這是所有的用戶界面。 –

+0

我已經通過設置.fields div樣式來顯示:無。這不是顯示問題,而是模型更新時不會觀察到_destroy。 – brad

0

除了增加hidedeleted記錄在用戶端,你可以在模型定製驗證​​添加跳過那些隱藏