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
Ответы
Ответ 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
Ответ 4
Взгляните на это:
https://rails.lighthouseapp.com/projects/8994/tickets/2815-nested-models-build-should-directly-assign-the-parent
Одна вещь, которую я делал в прошлом, добавляет: validates_presence_of :parent_id, :on => :update
. Не очень хорошо, но это помогает немного затянуть сеть.
Ответ 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 для динамического добавления/удаления полей.