嵌套记录无法验证,因为它的嵌套belongs_to不会保存

时间:2012-05-17 14:49:05

标签: ruby-on-rails validation nested-attributes belongs-to

我正在尝试创建一个嵌套的子孙记录。孩子属于父母和孙子。孩子不会对孙子进行验证_存在_因为它尚未保存。

我正在使用Rails 2.3.11,Formtastic,InheritedResources和Haml,其他一切似乎也能正常工作 - 例如,孙子的验证错误在父表单中正确填充,并且记住无效值并显示给用户。除非一切都有效,否则父模型甚至不会尝试更新。

我的代码是这样的,但是在不同的问题域中:

class Project < ActiveRecord::Base
  has_many :meetings, :dependent => :destroy
  accepts_nested_attributes_for :meetings
end

class Meeting < ActiveRecord::Base
  belongs_to :project
  belongs_to :task
  accepts_nested_attributes_for :task
  validates_presence_of :task_id, :project_id
end

class Task < ActiveRecord::Base
  has_many :meetings, :dependent => :destroy
end

项目已经存在,并且可能已经有我们不希望看到的会议。任务可能通过其他会议属于其他项目,但在这种情况下,任务和会议始终是新的。

在控制器中,我仅在新操作

上构建空白记录
@project.meetings.build

并保存这样的数据:

@project.update_attributes(params[:project])

并在视图中

- semantic_form_for @project do |f|
  - f.semantic_fields_for :meetings do |m|
    - next unless m.object.new_record?
    = m.semantic_errors :task_id
    - m.object.build_task unless i.object.task
    - m.semantic_fields_for :task do |t|
      - f.inputs do
        = t.input :task_field
        = m.input :meeting_field

当我尝试保存表单时,出现“任务不能为空”的验证错误。嗯,当然,任务还没有保存,我正在尝试验证,而且我没有它的ID。

是否有一种简单而优雅的方法可以确保在子记录之前构建孙子记录(Task)?

我在会议模型中尝试过类似的内容:

before_validation_on_create do |meeting|
  meeting.task.save if meeting.task.valid?
end

这似乎保存了任务,但会议仍然无法获得正确的ID。相同的错误,但创建了任务记录。

我也试过这个:

before_validation_on_create do |meeting|
  new_task = meeting.task.save if meeting.task.valid?
  meeting.task = new_task
end

提升ActiveRecord :: RecordNotFound有一种奇怪的行为“找不到具有ID = XX的任务会议ID =” - 我有点得到,但看起来像是一个红色的鲱鱼。

我还尝试在所有关系中添加:inverse_of并验证:task而不是:task_id。奇怪的是,后者失败但似乎没有给出任何错误信息。

我在这里的实际目标是创建多个任务,每个任务都有一个以前选择的项目的初始会议...所以我可以采取另一种方法解决我的问题 - 我可以在控制器中做一些简单和丑陋的事情,或者在项目的after_create中创建第一个会议。但这太漂亮了,太靠近工作了。我在以下问题上得到了正确的验证错误:task_field和:meeting_field意味着我在正确的轨道上。

我知道问题是什么,但不知道如何解决问题:我怀疑我错过了一些明显的问题。

谢谢!

1 个答案:

答案 0 :(得分:1)

嗯,我找到了一个解决方案,基于其中一个类似的问题,但缺点是&#34; rails 2.3似乎并不擅长这个。&#34;我想我能以比我见过的任何其他答案更简洁的方式提出答案。

您所做的是跳过:task_id的验证,但仅限于任务有效!我见过的大多数其他答案都使用了proc,但我认为使用委托更具可读性,如下所示:

delegate :valid?, :to => :task, :prefix => true, :allow_nil => true
validates_presence_of :task_id, :unless => :task_valid?

我还在水线下隐藏了另一个问题 - 在这种情况下,&#34;项目&#34;实际上是一种我想要保护的特殊记录,它有一个(故意)只为这个特殊记录失败的验证,而且我还设置了readonly?对于特殊记录来说是真的。

即使我实际上并没有更改该特殊记录,它仍然需要验证,并且无法通过它来更新子项。出于某种原因,我没有看到该验证的错误消息。为了解决这个问题,我对项目的验证仅适用:on =&gt; :创造,我拿出了readonly?的事情。

但是一般的解决方案是,如果对象本身有效,则不要验证未构建的belongs_to对象的存在。&#34; Nil永远不会有效,因此如果你只有一个object_id,验证仍然有效。

(除非你有答案或链接到一个问题,否则请不要对一个真诚的问题进行投票。我知道其他人已经通过其他方式询问了这个问题。那些其他问题,似乎没有一个问题,我没有找到解决方案。)