Я нашел этот ответ здесь, который должен решить мою проблему, но оказывается, что ответ предназначен для ассоциации "один ко многим". Кстати, я использую Rails 5.2
В моем «многие ко многим» у меня есть модель задачи, которая имеет_many от test_methods до tasks_test_methods, где tasks_test_methods - это таблица соединений.
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
Вот моя модель соединительного стола 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 проста и принимает только task_id и 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
У меня есть список предопределенных 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">
Я хочу иметь все эти test_methods в новой форме task вот так

Поэтому, когда пользователи устанавливают флажок с меткой 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
= 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 в параметры, также когда я нажимаю на 2 из test_methods и пытаюсь добавить задачу, например, я получаю это в консоли:
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





Наконец-то получил ответ на поставленную задачу. Весь мой подход был неправильным, я пытался вставить прямо в таблицу соединений (это никогда не сработает!).
эта статья помог мне понять мою ошибку, прочитав ее несколько раз. В итоге проблема была устранена за 3 минуты, после того как поиск решения занял около 3 дней.
В вашей модели примите вложенные атрибуты для объединенной таблицы следующим образом:
accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true
Читая документация по драгоценному камню кокона, я обнаружил, что это утверждение имеет смысл.
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>
Надеюсь это поможет.