2010-05-18 32 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 

我希望这对某人有帮助。