Подтвердить что ты не робот

Rails: проверка наличия parent_id в has_many ассоциации

У меня есть ресурс проектов, у которого много задач. Я хочу, чтобы каждая задача имела project_id, добавляя validates_presence_of :project_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
4b9b3361

Ответ 1

Ваш код работает:

  • Если вы validates_presence_of: project, то до тех пор, пока проект там, он будет проверять. Но если ваш проект не сохранен, вы все равно можете сохранить задачу.
  • Если вы 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

Ответ 2

См. здесь для окончательного ответа:

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

Не так элегантно, если вы спросите меня... Он должен прозрачно проверять.

Ответ 3

Может быть, я что-то не понимаю, но похоже, что вы пытаетесь обмануть рельсы. Почему бы вам просто не сделать так:

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

Ответ 5

Я думаю, что у тебя такая же проблема, с которой я имел дело. У меня две модели: учетная запись и пользователь, и когда создается учетная запись, первый пользователь создается с помощью @account.users.build. Модель пользователя имеет проверку validates_presence_of :account.

Чтобы выполнить проверку первого пользователя, я добавил следующий код модели моей учетной записи:

  before_validation_on_create :initialize_users

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

Ответ 6

На самом деле вам нужны оба:

validates_presence_of project
validates_presence_of project_id

Таким образом, задача не будет сохранена ни в одном из следующих случаев, предполагая, что в базе данных есть только 2 действительных проекта, то есть идентификатор проекта 99 недействителен:

task.project_id = 99
task.save

task.project = Project.new
task.save

Я надеюсь, что это кому-то поможет.

Ответ 7

Ваш класс Project должен определить

accepts_nested_attributes_for :tasks

Подробнее о том, как сделать форму, см. Вложенная модель формы в Railscasts.


EDIT:

В вашей форме вы должны иметь что-то вроде этого:

_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 и link_to_remove_fields - это методы, определенные в application_helper для динамического добавления/удаления полей.