嵌套表单问题并同时发布到两个关联表

Issue with nested forms and posting simultaneously to two associated tables

所以我现在已经尝试从不同的资源中提取多个小时来完成这项工作。我确定我可以用一种非常丑陋的方式将其与多个创建一起破解,但我确定有一种简单的方法可以做到这一点。

我正在尝试创建一个简单的时间和任务跟踪器。我有一个时间跟踪器 table 记录任务的开始和结束时间以及任务 ID。

有一个关联的 table,其中包含任务的标题和详细信息。

当我尝试.save 时!它

它放弃保存ActiveRecord::RecordInvalid(验证失败:任务必须存在):

逻辑似乎是它应该首先尝试创建创建 ID 的任务详细信息(标题和详细信息),然后它应该创建 timerecord(timetracker) 记录并将任务 ID 包含在 task_id时间记录栏。但是根据错误,猜测这不会发生。

感谢任何帮助我完成这项工作或指出我做错事的正确方向。

########## 编辑 发帖后,我意识到一个任务可以有多个时间记录,因为用户可以停止并重新启动同一个任务,该任务会创建具有相同任务 ID 的新记录。我已将任务模型从 has_one 更改为 has_many。 ###########

这是两个模型

class Timerecord < ApplicationRecord
  belongs_to :task
end

class Task < ApplicationRecord
  #has_one :timerecord #this has been changed to
  has_many :timerecord
  accepts_nested_attributes_for :timerecord   
end

在我的 Timetracker 控制器中#new & #create & private

def new
  @timetracker = Timerecord.new
  @timetracker.build_task
end

  def create
    @timetracker = Timerecord.new(timetracker_params)
      if @timetracker.save
        flash[:notice] = "Time Tracker Started"
        redirect_to timetracker_index_path
      end
  end

private #at the tail of permit you will see including the task attribute title
def timetracker_params
      params.require(:timerecord).permit(:user_id, :agency_id, :client_id, :task_id, :is_billable, :time_start, :time_end, :manual_input_hours, :timerecordgroup_id, :is_paused, :service_type_id, task_attributes: [:title])
  end

和表格


<%= form_with(model: @timetracker, url: timetracker_index_path, local: true) do |f| %>
  ... a bunch of fields for @timetracker
    
    <%#= fields_for :task, @timetracker.task do |t| %>
    <%#= t.text_field :title, class: "form-control shadow-sm", placeholder: "Title" %>
    <%# end %>

 ... more fields for timetracker 
<% end %>

首先,您必须知道哪个模型嵌套在另一个模型中,以及您要在哪个模型上调用 save 方法。

由于您的模型是:

class Timerecord < ApplicationRecord
  belongs_to :task
end

class Task < ApplicationRecord
  #has_one :timerecord #this has been changed to
  has_many :timerecord
  accepts_nested_attributes_for :timerecord   
end

您应该在 Task 模型上调用 save(这也会节省 timerecord)而不是在 TimeRecord 模型上,因为您的 Task 模型是接受 TimeRecord 模型的嵌套属性的模型,而不是相反的模型。

你的控制器应该看起来像这样:

def create
    @timetracker = Task.new(task_params)
      if @timetracker.save
        flash[:notice] = "Time Tracker Started"
        redirect_to timetracker_index_path
      end
  end

private 
def task_params
      params.require(:task).permit(#your permitted params for task, timetracker_attributes: [:title])
  end

如果您注意到,您的 Task 模型接受 timetracker 的嵌套属性,因此您应该允许 timetracker_attributes 而不是 task_attributes。 很容易知道您应该允许哪些嵌套参数,只需查看您的模型并查看 accept_nested_attributes_for 指的是哪个模型,然后在末尾添加 _attributes。

查看 accept_nested_attributes_for 文档,应该会有帮助 https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

不是这样的答案,只是一些建议,可以为您指明正确的方向,因为您的实施存在很多问题。

  1. 你不需要accepts_nested_attributes_for :timerecord 在任务中,因为您在 creating/editing 任务时没有创建时间记录。

  2. 您不需要在 TimerecordController 中为任务要求嵌套属性

  3. 表单应该有一个 hidden_field 表示 :task_id - 这就是您收到错误的主要原因。

  4. 但是您没有时间记录的关联任务,因此您还需要在生成新时间记录时分配它。您的 TimerecordController 新操作应该类似于:

def new
  @timetracker = Timerecord.new
  @timetracker.task_id = params[:task_id]
end
  1. 这意味着您还需要确保将 task_id 发送到新操作,例如 new_timerecord_path(task_id: my_task_instance.id)