2010-02-03 118 views
4

我在has_many關係上存在驗證問題,但是父項不存在。但是,在創建/保存父對象時,我想確保特定的子項(具有某些屬性)已經保存。對has_many關聯的Rails驗證

有一個Parent對象,has_manyChild對象。 Child對象首先被保存到數據庫中,因此沒有任何對父對象的引用。關聯結構是:

Parent 
    - has_many :children 

Child 
    - someProperty: string 
    - belongs_to: parent 

例如,有三個子對象:

#1 {someProperty: "bookmark", parent: nil} 
#2 {someProperty: "history", parent: nil } 
#2 {someProperty: "window", parent: nil } 

父纔有效,如果它包含孩子someProperty historywindow對象。

p = Parent.new(params[:data]) 
for type in %w[bookmark_id history_id window_id] 
    if !params[type].blank? 
     p.children << Child.find(params[type]) 
    end 
end 
// save the parent object p now 
p.save! 

當孩子們被分配到父與<<,他們沒有立即保存爲父母的ID不存在:

我爲建立父控制器內。而且父母要得救,至少必須有兩個孩子。我怎麼能解決這個問題?任何輸入是受歡迎的。

回答

5

不當然,爲什麼你需要做這樣的事情,但無論如何,這樣做呢?

class Parent < ActiveRecord::Base 

    CHILDREN_TYPES = %w[bookmark_id history_id window_id] 
    CHILDREN_TYPES.each{ |c| attr_accessor c } 

    has_many :children 

    before_validation :assign_children 
    validate :ensure_has_proper_children 

private 

    def assign_children 
    CHILDREN_TYPES.each do |t| 
     children << Child.find(send(t)) unless send(t).blank? 
    end 
    end 

    def ensure_has_proper_children 
    # Test if the potential children meet the criteria and add errors to :base if they don't 
    end 
end 

控制器:

... 
p = Parent.new(params[:data]) 
p.save! 
... 

正如你所看到的,我感動了所有的邏輯放在首位模型。然後,有兩步拯救孩子的過程。首先,我們將孩子分配給家長,然後驗證他們是否符合所需的標準(在那裏插入您的邏輯)。

對不起,我很抱歉。如有必要,我會回答任何進一步的問題。

+0

我喜歡這種方法,因爲它保持模型內的邏輯。 send(t)函數在這裏做什麼?它是否被父對象調用? – Anurag 2010-02-04 07:18:21

+0

是的,因爲我們將子類型動態地定義爲父對象的訪問者(屬性),所以我們也需要動態訪問它們。這就是爲什麼我們需要調用父對象的send方法。 – 2010-02-04 11:58:44

1

第一件事情,如果你想要孩子要保存而不父ID再有就是在做這個

p = Parent.new(params[:data]) 
for type in %w[bookmark_id history_id window_id] 
    if !params[type].blank? 
    p.children << Child.find(params[type]) 
    end 
end 

p.children << some_child 

整個目的沒有一點是附加父id到你不在這裏做的子對象,因爲父對象還不存在。

另一件事是,如果你只是想確保父母有一個孩子對象,並且如果你一起創建孩子和父母,那麼你可以使用父母和孩子創建周圍的事務塊,這將確保父母有孩子一樣

transaction do 
    p = create_parent 
    p.children << child1 
    p.children << child2 
end 

因此,本次交易中,如果在任何階段,代碼失敗,那麼它會回滾整個數據庫的事務,也就是你要麼有父母一方有2個孩子或沒有,如果這是最終狀態你正在尋找。

編輯:既然你不能創建一個父除非它有2個孩子,在這種情況下,而不是

p = Parent.new(params[:data]) 
for type in %w[bookmark_id history_id window_id] 
    if !params[type].blank? 
    p.children << Child.find(params[type]) 
    end 
end 

children = [] 
for type in %w[bookmark_id history_id window_id] 
    if !params[type].blank? 
    children << Child.find(params[type]) 
    end 
end 

if children.size >= 2 
    p = Parent.create!(params[:data]) 
    children.each {|child| p.children << child} 
end 

這是否有道理

+0

感謝您的回答。我並不特意要保存沒有父母身份的孩子,而只是在這個過程中始終創建孩子。那時我不會有任何有關父母的信息。父母的信息將出現在稍後的HTTP請求中。孩子的物品是一次性的,我不在乎他們是否沒有父母,因爲cron工作會定期清理它們。我可以在技術上使用會話,但這肯定會殺死服務器,因爲子對象真的很重,所以我選擇直接在數據庫中存儲em。 – Anurag 2010-02-03 07:05:20

+0

正確我明白你的意思,我會編輯我的答案,然後 – nas 2010-02-03 07:47:33