У меня есть 4 модели: Бизнес, Магазин, Продукт и МагазинПродукт.
У бизнеса много магазинов и продуктов
Я хочу, чтобы в магазине было много товаров, а товары должны принадлежать многим магазинам. При выборе продуктов, принадлежащих магазину, я также хочу указать количество каждого продукта. Это должно быть сделано с помощью вложенных форм в формах нового и редактирования магазина, и я использую компонент вложенной формы стимула.
Я получаю недопустимые параметры при сохранении формы магазина.
ошибка
Unpermitted parameters: :Product_ids, :quantity, :_destroy. Context: { controller:
StoresController, action: update, request: #<ActionDispatch::Request:0x00007f5cc927cfe0>,
params: {"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "store"=>{"name"=>"Arikeade Trendings, Challenge",
"Product_ids"=>"6", "quantity"=>"10", "_destroy"=>"false"},
"button"=>"", "controller"=>"stores", "action"=>"update", "business_id"=>"3", "id"=>"2"} }
магазин.рб
# validates presence, uniqueness, length and case-sensitivity of name attribute
validates :name, presence: true, uniqueness: {scope: :business_id, message: "Store name must be unique"}, length: { minimum: 3, maximum: 255 }
belongs_to :user
belongs_to :business
# store products association
has_many :store_products, inverse_of: :store, dependent: :destroy
has_many :products, through: :store_products, dependent: :destroy
# accepted nested form attributes
accepts_nested_attributes_for :store_products, reject_if: :all_blank, allow_destroy: true
end
store_product.rb
class StoreProduct < ApplicationRecord
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
belongs_to :product
belongs_to :store
end
продукт.рб
class Product < ApplicationRecord
...
belongs_to :user
belongs_to :business
# store products association
has_many :store_products, dependent: :destroy
has_many :stores, through: :store_products
'''
end
store_controller.rb
class StoresController < ApplicationController
...
def new
@store = Store.new
end
def create
@store = @business.stores.build(store_params)
@store.user = current_user
if @store.save
respond_to do |format|
format.html { redirect_to business_store_path(@business, @store), notice: 'store successfully created' }
end
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if @store .update(store_params)
respond_to do |format|
format.html { redirect_to business_store_url(@business, @store), notice: 'store successfully updated' }
end
else
render :edit, status: :unprocessable_entity
end
end
private
def store_params
params.require(:store).permit(:name, product_ids: [], store_products_attributes: [:id, :_destroy, :quantity, :product_id])
end
def find_store
@store ||= Store.find(params[:id])
end
def find_business
@business ||= Business.find(params[:business_id])
end
def find_products
@business = Business.find(params[:business_id])
@products ||= @business.products.pluck(:name, :id)
end
...
end
магазины/форма
<div data-controller='nested-form'>
<%= form_with model: [business, store] do |f| %>
<%= render 'shared/error_messages', f: f %>
<div class = "mb-3">
<%= f.text_field :name, placeholder: 'Store name', class: 'form-control' %>
</div>
<!--<div data-controller = "tom-select">
<small>Add products to store</small>
<%#= f.select :product_ids, @products, {}, { multiple: true, id: "select-products", class: 'mb-3 form-control' } %>
</div>-->
<div class = "d-block w-100 mt-3">
<h6><small>Add store products:</small></h6>
<hr>
</div>
<%# Custom logic for nested form %>
<template data-nested-form-target = "template">
<div class = "nested-form-wrapper row w-100" data-new-record = "<%= f.object.new_record? %>">
<div class = "col-md-4" data-controller = "tom-select">
<%= f.select :Product_id, @products, { include_blank: "Select Product" }, class: "form-control mb-3 form-control" %>
</div>
<div class = "col-md-4">
<%= f.number_field :quantity, placeholder: 'Quantity', class: "form-control" %>
</div>
<div class = "col-md-3">
<button type = "button" class = "btn btn-danger btn-sm border" style = "font-size: 10px;" data-action = "nested-form#remove">Remove product</button>
</div>
<%= f.hidden_field :_destroy %>
</div>
</template>
<!-- Inserted elements will be injected before that target. -->
<div data-nested-form-target = "target"></div>
<button type = "button" class = "btn btn-success w-100 mb-3" style = "font-size: 10px;" data-action = "nested-form#add">Add Product</button>
<div class = "submit">
<%= link_to business_stores_path(@business), class: "btn btn-light rounded-1 border me-2" do %>
<%= image_tag "x.png", width: "19" %>
<% end %>
<%= button_tag type: 'Submit', class: "btn btn-success rounded-1 border" do %>
<%= image_tag "check.png", width: "19" %>
<% end %>
</div>
<% end %>
</div>
Несколько вещей:
ваша форма создает параметры "Product_ids"
, но вы разрешаете :product_ids
в своем методе store_params
в контроллере (заглавная буква P — это ошибка).
ваш :store_product_attributes
в методе store_params
ожидает возврата хеша значений, например:
'store_product_attributes'=>{'quantity'=>'10', '_delete'=>'false'}
Вам нужно посмотреть сгенерированный HTML в вашей форме. Он должен создавать имена полей формы, такие как store_product_attributes[quantity]
и store_product_attributes[_delete]
.
Один из способов - это что-то вроде:
f.fields_for :product_attributes do |prod_attr|
...
<%= prod_attr.number_field :quantity, placeholder: 'Quantity', class: "form-control" %>
...
<%= prod_attr.hidden_field :_destroy %>
...
end
Я не в курсе стимулов, поэтому не знаю, как это на это повлияет. Но это стандартный код для получения вложенных атрибутов в параметрах формы.
Я только что понял это и ответил на свой вопрос. Спасибо большое, что указали на мои ошибки. Мой подход был совершенно неправильным.