问题
I found this answer here which should be able to solve my problem but turns out that answer is for a one to many association. Btw, i'm using Rails 5.2
In my many to many, I have a Task model which has_many test_methods through tasks_test_methods where tasks_test_methods is a join 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
Here is my join table model tasks_test_methods
class TasksTestMethod < ApplicationRecord
belongs_to :task
belongs_to :test_method
accepts_nested_attributes_for :test_method, :reject_if => :all_blank
end
And my TestMethod model
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 is simple and only receives the task_id and test_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
I have a list of predefined test_methods that are displayed and needs to be added to a task upon creation. For example.
<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">
I want to have all these test_methods in the new task form like this
So when users checks a checkbox with label Visual Testing a new record is created like this (an example from the rails console):
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>]>
This is my new task form, here I'm using cocoon gem already in the fields_for part
= 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
And in the test_method_fields partial I have this:
= 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
The above code shows the checkboxes as expected, but doesn't work as expected. Can someone guide me on how to make this work?
I have also whitelisted these in the TasksController
tasks_test_methods_attributes: [:id, test_method_ids: []])
This is the code form produces for each checkbox in the browser console
<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>
The problem is I can't get the tasks_test_methods_attributes into the params, also when I click on 2 of the test_methods and try to add a task, for example, I get this in the console:
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"}
When I try to create a Task, it's been created but without the tasks_test_methods_attributes part in the params.
This is my 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
回答1:
Finally got an answer for this task. My whole approach was wrong, trying to insert directly into a join table (That's never going to work!).
This article helped me figure out my mistake, after reading it multiple times. Ended up fixing the problem in 3 mins, after taking about 3 days to search for a solution.
In your model, accept nested attributes for the join table like so:
accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true
While reading the cocoon gem documentation, I found this statement that makes sense.
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
Now in my Task model, I have
has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy
has_many :test_methods, :through => :tasks_test_methods
Also in my TestMethod model, I have
has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy
has_many :tasks, :through => :tasks_test_methods
And then in the TasksController I added this to the params, test_method_ids: []
And finally in the form I have this:
.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
And when now your HTML element should look like this:
<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>
Hope this helps.
来源:https://stackoverflow.com/questions/50397979/update-multiple-checkboxes-on-association-model-through-nested-attributes-with-c