2010-05-18 59 views
20

我有一個包含許多任務的項目資源。我想通過將validates_presence_of :project_id添加到任務模型來確保每個任務都有project_idRails:在has_many關聯中驗證parent_id的存在

但是,當創建一個包含任務的新項目時,project_id在記錄保存之前將不可用,因此我不能使用validates_presence_of :project_id

所以我的問題是,我如何驗證任務模型中的project_id的存在?我想確保每項任務都有一位家長。

...

class Project < ActiveRecord::Base 

    has_many :tasks, :dependent => :destroy 
    accepts_nested_attributes_for :tasks, :allow_destroy => true 

...

class Task < ActiveRecord::Base 

belongs_to :project 
validates_presence_of :project_id 
+0

這個問題對我來說沒有多大意義。你想要一個任務屬於一個項目,而不需要從一個項目開始...如何獲得一個不存在的東西的id? – porkeypop 2010-06-08 05:19:11

+3

您是否在創建項目時通過嵌套表單創建任務? – 2010-06-08 21:45:29

回答

17

你的代碼的工作:

  • 如果validates_presence_of:項目,那麼只要該項目是存在的,它會驗證。但是,如果您的項目未保存,您仍然可以保存任務。
  • 如果您validates_presence_of:project_id,那麼整數必須在那裏,指示一個保存的值。

這裏是rSpec,證明了這一點。如果驗證:project_id,則不能保存任務而不保存項目。

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

/specs/model_specs/task_spec。RB

require File.dirname(__FILE__) + '/../spec_helper' 

describe Task do 

    before(:each) do 
    @project = Project.new 
    end 

    it "should require a project_id, not just a project object" do 
    task = Task.new 
    task.project = @project 
    Task.instance_eval("validates_presence_of :project_id") 
    task.valid?.should == false 
    end 

    it "should not be valid without a project" do 
    task = Task.new 
    task.project = @project 
    Task.instance_eval("validates_presence_of :project") 
    task.valid?.should == false 
    task.save.should == false 
    end 

end 
+0

我想標記您的答案正確,但複選標記已消失:/ – deb 2010-06-11 13:10:13

+0

謝謝deb!也許它會很快回來。 – 2010-06-11 16:50:37

0

Project類必須定義

accepts_nested_attributes_for :tasks 

關於如何使形式更加詳見Nested Model Form on Railscasts


編輯:

在你的表格,你應該有這樣的事情:

_form.html.erb

<% form_for @project do |f| %> 
    # project fields... 
    <% f.fields_for :tasks do |builder| %> 
     <%= render 'task_fields', :f => builder %> 
    <% end %> 
    <p><%= link_to_add_fields "Add task", f, :tasks %></p> 
    <%= f.submit %> 
<% end %> 

_task_fields.html.erb

<%= f.label :name, "Task name:" %> 
<%= f.text_field :name %> 
# task fields... 
<%= link_to_remove_fields "Delete task", f, :tasks %> 

link_to_add_fieldslink_to_remove_fields是在application_helper中定義的用於動態添加/刪除字段的方法。

+0

問題是如何使project_id對任務是強制性的。 – deb 2010-05-18 22:46:32

+0

我發佈了一個幫助澄清的代碼示例。 謝謝! – deb 2010-05-18 23:22:45

+0

hmn - 很好的問題。所以當他們一起保存時,它不會首先創建項目,然後執行任務? – digitalWestie 2010-06-07 11:33:32

3

也許我不明白的東西,但它看起來像你試圖欺騙鐵軌。你爲什麼不只是這樣做:

class Task < ActiveRecord::Base 
    belongs_to :project 
    validate_presence_of :project 
end 
+0

這是一個超級老問題:但是,你不能做一個簡單的關聯驗證的原因是因爲任務在: fields_for ...它們嵌套在:項目表單中。保存時,project_id將不可用於驗證,驗證將失敗。您可以通過簡單地刪除項目的驗證來測試它。 :inverse_of確保關聯在保存時完好無損。 https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations – hellion 2016-01-22 15:37:28

2

在此請看:

https://rails.lighthouseapp.com/projects/8994/tickets/2815-nested-models-build-should-directly-assign-the-parent

一件事,我在過去做的是添加:validates_presence_of :parent_id, :on => :update。不是很好,但它有助於收緊網絡。

+0

太棒了!這像一個魅力。 – molf 2010-07-29 10:27:06

+0

這完全忽略了創建操作。不好。使用:inverse_of確保您的父對象在保存嵌套字段時關聯。 https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations – hellion 2016-01-22 15:38:45

15

了明確的答案見here

class Project < ActiveRecord::Base 

    has_many :tasks, :dependent => :destroy, :inverse_of => :project 
    accepts_nested_attributes_for :tasks, :allow_destroy => true 

class Task < ActiveRecord::Base 

belongs_to :project 
validates_presence_of :project 

不那麼優雅,如果你問我...應該透明地驗證。

+0

謝謝我會研究「inverse_of」 – deb 2010-08-19 19:08:50

2

我認爲你遇到了同樣的問題。我有兩個模型,賬戶和用戶,當賬戶創建時,第一個用戶通過@account.users.build創建。用戶模型具有validates_presence_of :account驗證。

爲了使第一用戶通過驗證,我下面的代碼添加到我的帳戶型號:

before_validation_on_create :initialize_users 

    def initialize_users 
    users.each { |u| u.account = self } 
    end 
1

在現實中,你需要兩個:

validates_presence_of project 
validates_presence_of project_id 

這樣的任務將不會被保存在以下任一情況下,假設您在數據庫中只有2個有效項目,即項目ID 99無效:

task.project_id = 99 
task.save 

task.project = Project.new 
task.save 

我希望這對某人有幫助。