Вложенные формы в Ruby с флажками и отношением «многие ко многим»

#ruby-on-rails #ruby #rails-activerecord #strong-parameters

#ruby-on-rails #ruby #rails-activerecord #строгие параметры

Вопрос:

Я новичок в ruby, и у меня возникли проблемы в следующем сценарии. Я пытаюсь построить взаимосвязь между счетом и товарами. В моем случае я хочу сгенерировать счет во время выполнения, например, когда пользователь нажимает «Создать новый счет», он направляется на маршрут, подобный http://localhost:3000/bills/new и затем у него есть список элементов, из которых он должен выбрать, установив флажки и добавив количество. У меня есть 3 таблицы, элементы, счета, BillItems. В них есть следующие поля:

     create_table "bill_items", force: :cascade do |t|
    t.integer "bill_id"
    t.integer "item_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "quantity"
  end

  create_table "bills", force: :cascade do |t|
    t.integer "user_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

    create_table "items", force: :cascade do |t|
    t.string "name"
    t.float "price"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "category_id"
  end  
  

Мои модели созданы следующим образом:

Bill.rb

  class Bill < ApplicationRecord
        has_many :bill_items
        has_many :items, through: :bill_items
        accepts_nested_attributes_for :bill_items, :allow_destroy => true, :reject_if => :all_blank
    end
  

Item.rb

 class Item < ApplicationRecord
    
    has_many :bill_items
    has_many :bills, through: :bill_items

end
  

BillItem.rb

 class BillItem < ApplicationRecord
    belongs_to :bill
    belongs_to :item
    
end
  

У меня есть такая форма, как:

 <%= form_for @bill do |f| %>
 <% if @allItems %>

     <% @allItems.each_with_index do |item, index| %>
         <tr class="table-success" scope="col-8">
        <%= f.fields_for :bill_items do |s| %>
            <td class="text-secondary"><%= item.category.name %></td>
            <%= s.hidden_field :name, value: item.name %>
            <td class="text-primary"><%= s.label item.name %></td>
            <td><%= check_box_tag "item_ids[]", item.id, false, class: 'selectable' %> </td> 
            <td><%= s.number_field(:quantity, in: 1.0..100.0, step: 1) %></td>
            <td><%= s.label  :price, item.price %></td>
           
        <% end %>
        </tr>
     <% end %>
 <% end %>
    </tbody>
</table>
   <div class="form-group row justify-content-center">
    <%= f.submit "Create Order with Selected items", class: "btn btn-secondary" %>
    </div>
<% end %>
  

Затем у меня есть настройка моего контроллера следующим образом:

 def new
    @bill = Bill.new
    @bill_items = @bill.bill_items.build
    
end

def create 
    byebug
    @bill = Bill.new(bill_params)
    @bill.save
    redirect_to new_bill_path

end

private 
    def bill_params
         params.require(:bill).permit(bill_items_attributes: [:quantity, :item_ids])
    end
  

Когда я запускаю свой код и отправляю данные в форму и проверяю параметры через byebug, он показывает мне следующие параметры, в то время как я выбрал два элемента с идентификаторами 1 и 4:

 <ActionController::Parameters {"authenticity_token"=>"hVnrTkWxWwuXqS4tb01INVkNwRaFooVERKe2L8YkXykyPqImKCVRrvqjhK8sA0Q26nsOS dSNdLvIOPTfis8nQ==", "bill"=>{"bill_items_attributes"=>{"0"=>{"name"=>"sheer", "quantity"=>"2"}, "1"=>{"name"=>"burger", "quantity"=>""}, "2"=>{"name"=>"custurs", "quantity"=>""}, "3"=>{"name"=>"sib", "quantity"=>"4"}}}, "item_ids"=>["1", "4"], "commit"=>"Create Order with Selected items", "controller"=>"bills", "action"=>"create"} permitted: false>
  

Затем я нажимаю «Отправить», и это только сохраняет счет в БД и выдает мне ошибку

 Unpermitted parameter: :name
Unpermitted parameter: :name
Unpermitted parameter: :name
Unpermitted parameter: :name`
  

Я перепробовал много методов и не смог найти решение. Будет очень полезно, если кто-нибудь сможет мне с этим помочь. Даже если мне нужно перепроектировать свою логику, помогите мне с этим. Спасибо.

Ответ №1:

 <td><%= check_box_tag "item_ids[]", item.id, false, class: 'selectable' %> </td>
  

должно быть

 <td><%= check_box_tag "bill[item_ids[]]", item.id, false, class: 'selectable' %> </td>
  

Комментарии:

1. пожалуйста, дайте мне знать о любых других изменениях, которые необходимо внести в параметры моего контроллера или модальные параметры. Спасибо

Ответ №2:

Проблема: существует несоответствие в именовании вложенных атрибутов

Модель Bill принимает вложенные атрибуты для :items указанного вами контроллера :bill_items_attributes и формирует генерирующие поля для bill_items — f.fields_for :bill_items

Решение: сделайте его согласованным

В модели Bill —

accepts_nested_attributes_for :items

На контроллере счетов —

permit(items_attributes:

В форме new.html.erb —

f.fields_for :items

Однако это создаст еще одну проблему для items_id, которую я не уверен, чего там пытаются достичь.

Ответ №3:

Вы получали эти ошибки, потому что у вас было hidden_field :name в форме, которая не требуется при отправке формы. Я удалил это. Однако возникла проблема с сильными параметрами и наименованием флажков. Я исправил сильные параметры и сохранил название флажка описательным, чтобы вы могли видеть, как форма будет его генерировать.

Попробуйте этот код. Я смог создавать записи с помощью этого кода.

bills_controller.rb

 private 
    def bill_params
         params.require(:bill).permit(bill_items_attributes: [:quantity, :item_id])
    end
  

new.html.erb

 <%= form_for @bill do |f| %>
    <% if @allItems %>
        <% @allItems.each_with_index do |item, index| %>
            <%= f.fields_for :bill_items do |s| %>
                <tr class="table-success" scope="col-8">
                    <td class="text-primary"><%= s.label item.name %></td>
                    <td><%= check_box_tag "bill[bill_items_attributes][#{index}][item_id]", item.id, false, class: 'selectable' %> </td> 
                    <td><%= s.number_field(:quantity, in: 1.0..100.0, step: 1) %></td>
                    <td><%= s.label  :price, item.price %></td>
                </tr>
            <% end %>
        <% end %>
    <% end %>
   
    <div class="form-group row justify-content-center">
        <%= f.submit "Create Order with Selected items", class: "btn btn-secondary" %>
    </div>
<% end %>