问题
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