Вы получаете сообщение об ошибке «unpermitted params category_ids», потому что вам сначала нужно объявить в своей модели продукта следующее:
accepts_nested_attributes_for :categories , allow_destroy: true
Как только это будет сделано, вы должны начать получать всю информацию о category_ids, действительно вложен в ваши параметры.
Однако я полностью рекомендую НЕ выполнять в ваших представлениях и партиалах запрос ActiveRecord к вашей БД. Например:
<div class="dropdown-trigger btn">
<%= f.collection_select(:category_ids, Category.all, :id, :name) %>
</div>
Это не рекомендуется. Вместо этого вы должны получить от вашего контроллера полный набор категорий. Единственная функция представления в этом случае - заполнить данные пользователем, выбрать категории, а затем после отправки отправить всю эту информацию обратно в контроллер. Вот и все. Не выполняет никаких запросов. Это правда, что вы можете это сделать. Я имею в виду, что это физически возможно сделать там в этом представлении или даже сделать это на помощнике (тоже неправильно, помощник должен выполнять дополнительные действия над ресурсами, уже загруженными или полученными от контроллеров), но MVC означает разделение обязанностей по нескольким причинам.
В любом случае, в вашем случае я бы выбрал go более или менее примерно так:
На products_controller.rb:
def edit
@categories_to_assign = product_service.get_categories_to_assign(@product)
end
def product_service
ProductService
end
def product_params
params.require(:product).permit(:name, :price, :description, :image, categories_to_assign: [])
end
На product_service.rb он получает категории:
def self.get_categories_to_assign(product)
categories_scope.where.not(id: product.categories.map(&:id)).map do |category|
["#{category.name}", category.id]
end
end
def self.categories_scope()
Category
end
Затем при редактировании / новом представлении:
<%
categories_to_assign = @categories_to_assign || []
%>
<% content_for :products_main_content do %>
<div id="edit_product_content">
<%= render partial: 'products/form', locals: {
product: product,
return_to: return_to,
categories_to_assign: categories_to_assign
} %>
</div>
<% end %>
Затем на _form. html .erb partial:
<%
categories_to_assign = local_assigns.fetch(:categories_to_assign, [])
%>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title"><%= t('products.categories.title') %></h2>
</div>
<div class="panel-body">
<div class="form-horizontal" id="categories_container" data-sjr-placeholder>
<%= render partial: 'products/categories', locals: {f: f, categories_to_assign: categories_to_assign} %>
</div>
</div>
</div>
И, наконец, на _categories. html .erb partial:
<%
categories_to_assign = local_assigns.fetch(:categories_to_assign, [])
%>
<% if categories_to_assign.present? %>
<%= select_tag "#{f.object_name}[categories_to_assign][]", options_for_select(categories_to_assign), {id: "#{f.object_name}_categories_to_assign", include_blank: true, multiple: true, class: 'form-control', data: {placeholder: t('products.form.select_category')}} %>
<% end %>
Как видите, общая идея заключается в передаче соответствующей информации от контроллера после того, как она была правильно получена в product_service (вы должны добавить ее), а затем она переходит в редактирование / новое представление, а затем оно, наконец, переходит во вложенные частичные файлы. Таким образом, все будет разделено по сферам ответственности.