2009-06-01 79 views
69

我在我的一個Rails模型中使用了accep_nested_attributes_for,並且我想在創建父項之後保存子項。accep_nested_attributes_for兒童關聯驗證失敗

表單完美,但驗證失敗。爲了簡便起見設想以下:

class Project < ActiveRecord::Base 
    has_many :tasks 
    accepts_nested_attributes_for :tasks 
end 

class Task < ActiveRecord::Base 
    belongs_to :project 

    validates_presence_of :project_id 
    validates_associated :project 
end 

而且我運行:

Project.create!(
    :name => 'Something', 
    :task_attributes => [ { :name => '123' }, { :name => '456' } ] 
) 

在節能項目模型,驗證失敗的任務,因爲他們沒有一個PROJECT_ID(因爲項目尚未保存)。

好像Rails正在按以下模式:

  • 驗證項目
  • 驗證任務
  • 保存項目
  • 保存任務

的模式應該是:

  • 驗證項目
  • 傳給:保存項目,並繼續...
  • 驗證任務
    • 傳給:保存任務
    • 在失敗:刪除項目(也許回滾?)

所以我的問題歸結爲:我怎樣才能讓Rails運行project_id =(或project =)方法和父對象之後的子任務(任務)驗證(項目)已保存,但如果任何子項(任務)無效,則不保存父項(項目)模型?

任何想法?

回答

12

使用該答案爲Rails 2,否則請參閱下面的:inverse_of答案

您可以解決此通過爲PROJECT_ID檢查,如果相關的項目是有效的。


class Task < ActiveRecord::Base 
    belongs_to :project 

    validates_presence_of :project_id, :unless => lambda {|task| task.project.try(:valid?)} 
    validates_associated :project 
end 
+0

這並沒有爲我工作。在validates_presence_of中:project_id調用'project'返回nil,導致它嘗試驗證project_id並使驗證失敗。我創建了另一個問題,因爲我認爲問題是不同的,但它似乎是相同的http://stackoverflow.com/questions/2102724/rails-nested-attributes-association-validation-failing。 – 2010-01-20 18:11:16

+10

對於Rails 3.0和更高版本,下面的inverse_of解決方案更加正確。 – nertzy 2011-10-18 17:26:54

1

你可以只創建項目且僅當它通過驗證加項目:

tasks = params.delete(:task_attributes) 
if Project.create(params) 
    Project.update_attributes(:task_attributes => tasks) 
end 

0

相反的是戈指出,這並不總是可以接受的,然後再保存父對象這些孩子。通常你想在開始保存之前確認所有的對象都是有效的。這使用戶有機會重新編輯輸入表單並更正任何錯誤。

您描述的問題將在Rails 3.0中修復。我會發佈一個鏈接到燈塔票,但stackoverflow.com不允許這樣做,因爲我是一個新用戶(#fail)。但暫時,你可以使用插件「parental_control」,這將修復你的「錯誤」。

2

不幸的是,以上建議都不適用於Rails 2.3.5。

就我而言,如果兩者都是使用嵌套屬性創建的,則任務中的項目始終爲零。只有當我刪除validates_presence_of時,創建纔會成功。單元測試和日誌顯示所有內容都已正確創建。

因此,我現在傾向於將約束添加到數據庫而不是Rails,因爲這似乎更加可靠。

8

只有驗證的關係,而不是ID:

class Task < ActiveRecord::Base 
    belongs_to :project 

    validates_presence_of :project 
end 

只要關聯填充,ActiveRecord的會考慮驗證已經成功,該模型是否被保存。你可能想調查自動保存爲好,以確保任務的項目總是被保存:

class Task < ActiveRecord::Base 
    belongs_to :project, :autosave => true 

    validates_presence_of :project 
end 
+0

最後,我正在尋找答案。 – doug 2013-03-23 17:28:05

159

使用:inverse_ofvalidates_presence_of :parent。這應該可以解決您的驗證問題。

class Dungeon < ActiveRecord::Base 
    has_many :traps, :inverse_of => :dungeon 
    end 

    class Trap < ActiveRecord::Base 
    belongs_to :dungeon, :inverse_of => :traps 
    validates_presence_of :dungeon 
    end 

http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_presence_of

https://github.com/rails/rails/blob/73f2d37505025a446bb5314a090f412d0fceb8ca/activerecord/test/cases/nested_attributes_test.rb