简体   繁体   English

Rails:验证has_many关联中parent_id的存在

[英]Rails: validate presence of parent_id in has_many association

I have a projects resource that has many tasks. 我有一个项目资源,有很多任务。 I want to ensure that every task has a project_id by adding validates_presence_of :project_id to the tasks model. 我想通过将validates_presence_of :project_id添加到任务模型来确保每个任务都有project_id

However, when creating a new project with tasks, the project_id won't be available until the record saves, therefore I can't use validates_presence_of :project_id . 但是,在使用任务创建新项目时,在记录保存之前project_id将不可用,因此我无法使用validates_presence_of :project_id

So my question is, how do I validate presence of project_id in the task model? 所以我的问题是,如何在任务模型中验证project_id的存在? I want to ensure every task has a parent. 我想确保每个任务都有父母。

... ...

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

Your code works: 你的代码有效:

  • If you validates_presence_of :project, then as long as the project is there, it will validate. 如果您验证了_presence_of:project,那么只要项目在那里,它就会验证。 But if your project is unsaved, you could still save the task. 但是如果你的项目未保存,你仍然可以保存任务。
  • If you validates_presence_of :project_id, then the integer must be there, indicating a saved value. 如果您使用validates_presence_of:project_id,那么整数必须在那里,表示已保存的值。

Here's rSpec that proves the point. 这是证明重点的rSpec。 If you validate :project_id, you can't save a task without saving the Project. 如果验证:project_id,则无法保存任务而不保存项目。

class Task < ActiveRecord::Base
  belongs_to :project
end

/specs/model_specs/task_spec.rb /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

See here for the definitive answer : 请看这里的最终答案:

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

Not so elegant if you ask me... It should transparently validate. 如果你问我就不那么优雅......它应该透明地验证。

Maybe I don't understand something, but it looks like you are trying to cheat rails. 也许我听不懂东西,但看起来你正试图欺骗铁轨。 Why don't you just do like this: 你为什么不这样做:

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

Take a look at this: 看看这个:

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

One thing I have done in the past is add: validates_presence_of :parent_id, :on => :update . 我过去做的一件事是添加: validates_presence_of :parent_id, :on => :update Not great but it helps tighten the net a little. 不是很好,但它有助于收紧网。

I think you're having the same issue I dealt with. 我认为你遇到了同样的问题。 I have two models, Account and User, and when the account is created the first user is created through a @account.users.build . 我有两个模型,帐户和用户,并且在创建帐户时,第一个用户是通过@account.users.build创建的。 The User model has a validates_presence_of :account validation. User模型具有validates_presence_of :account验证。

To make the first user pass validation, I added the following code to my Account model: 为了使第一个用户通过验证,我将以下代码添加到我的帐户模型中:

  before_validation_on_create :initialize_users

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

In reality you need both: 实际上你需要两个:

validates_presence_of project
validates_presence_of project_id

That way the task will not be saved in either of the following cases assuming that you have only 2 valid projects in the database, ie project id 99 is invalid: 这样,假设您在数据库中只有2个有效项目,即项目ID 99无效,则不会在以下任何一种情况下保存任务:

task.project_id = 99
task.save

task.project = Project.new
task.save

I hope this is of help to someone. 我希望这对某人有帮助。

Your Project class must define 您的Project类必须定义

accepts_nested_attributes_for :tasks

See Nested Model Form on Railscasts for more details on how to make the form. 有关如何制作表单的更多详细信息,请参阅Railscast上的嵌套模型表单


EDIT: 编辑:

In your form you should have something like this: 在你的表格中你应该有这样的东西:

_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_fields and link_to_remove_fields are methods defined in application_helper to add/delete fields dynamically. link_to_add_fieldslink_to_remove_fields是application_helper中定义的方法,用于动态添加/删除字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM