使用 Cocoon gem in Rails 通过嵌套属性更新关联模型上的多个复选框

Update multiple checkboxes on association model through nested attributes with Cocoon gem in Rails

我找到了这个答案 ,它应该能够解决我的问题,但事实证明这个答案是针对一对多关联的。顺便说一句,我正在使用 Rails 5.2

在我的多对多中,我有一个 has_many test_methodstasks_test_methods 的任务模型,其中 tasks_test_methods 是一个连接 table。

class Task < ApplicationRecord
  has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy 
  has_many :test_methods, :through => :tasks_test_methods

  accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true
end

这是我加入的table模型tasks_test_methods

class TasksTestMethod < ApplicationRecord 
  belongs_to :task
  belongs_to :test_method

  accepts_nested_attributes_for :test_method, :reject_if => :all_blank
end

还有我的 TestMethod 模型

class TestMethod < ApplicationRecord
  has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy 
  has_many :tasks, :through => :tasks_test_methods
end

My TasksTestMethods table 很简单,只接收 task_idtest_method_id

create_table "tasks_test_methods", id: false, force: :cascade do |t|
    t.bigint "task_id", null: false
    t.bigint "test_method_id", null: false
end

我有一个显示的预定义 test_methods 列表,需要在创建时添加到 task。例如。

<TestMethod id: 1, name: "Visual Testing (VT)", code: nil, description: nil, category_id: 1, created_at: "2018-05-15 12:11:38", updated_at: "2018-05-15 12:11:38">

我想在新的 task 表格中包含所有这些 test_methods

因此,当用户选中带有标签 Visual Testing 的复选框时,会像这样创建一条新记录(来自 rails 控制台的示例):

2.5.1 :176 > params = { task: { name: "Test", client_id: 1, tasks_test_methods_attributes: [test_method_id: 1]}}
 => {:task=>{:name=>"Test", :client_id=>1, :tasks_test_methods_attributes=>[{:test_method_id=>1}]}}
2.5.1 :177 > task = Task.create!(params[:task])
<ActiveRecord::Associations::CollectionProxy [#<TasksTestMethod task_id: 16, test_method_id: 2>]>

这是我的新 task 表格,我在 fields_for 部分

中使用了 cocoon gem
= form_with(model: @task) do |f|
  .form-group
     %label Client Name:
        = f.select(:client_id, @clients.collect { |client| [ client.name, client.id ] }, { include_blank: "Select Client" }, { :class => 'form-control select2', style: 'width: 100%;' })
   .col-md-12
      = f.fields_for :tasks_test_method do |task_test_method|
        = render 'test_method_fields', f: task_test_method

test_method_fields 部分我有这个:

= collection_check_boxes :tasks_test_method, :test_method_ids, @test_methods, :id, :name do |box| 
  .form-group.row 
    .form-check.col-md-3
      = box.check_box
      %span.ml-2 
        = box.label

以上代码按预期显示了复选框,但未按预期工作。有人可以指导我如何完成这项工作吗?

我也在 TasksController

中将它们列入白名单
tasks_test_methods_attributes: [:id, test_method_ids: []])

这是为浏览器控制台中的每个复选框生成的代码形式

<div class="form-check col-md-3">
  <input type="checkbox" value="1" name="tasks_test_method[test_method_ids][]" id="tasks_test_method_test_method_ids_1">
  <span class="ml-2">
   <label for="tasks_test_method_test_method_ids_1">Visual Testing (VT)</label>
  </span>
</div>

问题是我无法将 tasks_test_methods_attributes 放入参数中,当我单击 test_methods 中的 2 个并尝试添加任务时,例如,我将其放入控制台:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"cuEnQNeh4iT+38AwsNduQGce5kxFcS7sFw0SAgpdvxJjVziFTnrSgzdq6LODNDxzhan2ne31YMeCcJdYL8VoSQ==", "task"=>{"client_id"=>"", "name"=>"", "department_id"=>"", "start_date"=>"", "offshore_flag"=>"0", "order_number"=>"", "description"=>"", "task_manager_id"=>"", "language_id"=>"", "client_ref"=>"", "testsite"=>"", "contact_person_phone"=>"", "contact_person"=>"", "contact_person_email"=>"", "information_to_operator"=>""}, "tasks_test_method"=>{"test_method_ids"=>["", "1", "2"]}, "commit"=>"Create Task"} 

当我尝试创建 Task 时,它已创建但参数中没有 tasks_test_methods_attributes 部分。

这是我的TasksController new action

def new
  @task = Task.new 
  TestMethod.all.each do |test_method|
    @task.tasks_test_methods.build(test_method_id: test_method.id)
  end
 end

终于得到了这个任务的答案。我的整个方法都是错误的,试图直接插入连接 table(这永远行不通!)。

This article 多次阅读后帮助我找出了错误。在花了大约 3 天时间寻找解决方案后,最终在 3 分钟内解决了问题。

在您的模型中,接受连接 table 的嵌套属性,如下所示:

accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true

在阅读 cocoon gem documentation 时,我发现这句话很有道理。 When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :test_method

现在在我的 Task 模型中,我有

has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy 
has_many :test_methods, :through => :tasks_test_methods

同样在我的 TestMethod 模型中,我有

has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy 
has_many :tasks, :through => :tasks_test_methods

然后在 TasksController 中我将其添加到参数中,test_method_ids: []

最后我的形式是这样的:

.form-group
   = f.collection_check_boxes :test_method_ids, @test_methods, :id, :name do |test_method|
      .form-check.mb-4
         = test_method.check_box(class: "form-check-input")
         %label.form-check-label 
            %span.ml-2  
              = test_method.label

现在您的 HTML 元素应该如下所示:

<div class="form-check mb-4">
  <input class="form-check-input" type="checkbox" value="1" name="task[test_method_ids][]" id="task_test_method_ids_1">
  <label class="form-check-label">
   <span class="ml-2">
     <label for="task_test_method_ids_1">Visual Testing (VT)</label>
   </span>
  </label>
</div>

希望这对您有所帮助。