Search code examples
ruby-on-railsactiverecordcocoon-gem

Double nested form not sending attributes to controller


All help/hints/debugging tips/thoughts are welcome, as I'm pretty much stuck for some time on this issue.

I have a 2-level deep nested form. The parameters of the 1st level are saving correctly (e.g. options_attributes), but unfortunately the parameters of the deepest form are not being sent to the controller (.e.g. option_prices_attributes is not shown at all in my parameters. I use the cocoon gem to create a dynamic nester form.

Interestingly,

(1) in my console I am able to create a 2-level deep object where also the option_price parameters are saving.

(2) when using :option_prices_attributes in the simple_field forms, they are being sent as parameters:

{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"P6oCZkZF8O+, "accommodation_category"=>{"options_attributes"=>{"1568305809712"=>{"name"=>"Option name", "_destroy"=>"false", "option_prices_attributes"=>{"name"=>"Option price", "_destroy"=>"0"}}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"96"}

=> and consequently resulting in an error message in the terminal saying no implicit conversion of Symbol into Integer

My models

class AccommodationCategory < ApplicationRecord
  belongs_to :park

  has_many :options, dependent: :destroy
  accepts_nested_attributes_for :options, allow_destroy: true
end


class Option < ApplicationRecord
  belongs_to :accommodation_category

  has_many :option_prices, dependent: :destroy
  accepts_nested_attributes_for :option_prices, allow_destroy: true
end


class OptionPrice < ApplicationRecord
  belongs_to :option
end

Accommodation_categories_controller.rb

class AccommodationCategoriesController < ApplicationController
  # skip_before_action :authenticate_user!

[...]

  def update
    @park = Park.find(params[:park_id])
    @accommodation_category = @park.accommodation_categories.find(params[:id])
    authorize @accommodation_category
    @accommodation_category = @accommodation_category.update_attributes(accommodation_category_params)
  end

  def new_options
    @accommodation_category = AccommodationCategory.find(params[:id])
    @park = @accommodation_category.park
    authorize @accommodation_category
    #2nd level nesting
    # @accommodation_category.options.build
    @accommodation_category.options.each do |option|
      option.option_prices.build
    end
  end

  private
  def accommodation_category_params
    params.require(:accommodation_category).permit(:name, :description, :status, :persons_max, :persons_min, :persons_included, :accommodation_count, :enabled_accommodation_count, :thumb, :included_services, :photo,
      options_attributes: [:name, :description, :_destroy,
        option_prices_attributes: [:name, :price_type, :start_date, :end_date, :price, :duration, :duration_min, :duration_max, :backend_only, :weekend_extra, :_destroy]])
  end
end

views/accommodation_categories/new_options.html.erb

<%= render 'options_new_form', park: @park%>

views/accommodation_categories/options_new_form.html.erb (1st level)

<%= simple_form_for [@park, @accommodation_category] do |f|%>

<h1> <%= @accommodation_category.name %> </h1>

<% @accommodation_category.options.each do |option| %>
  <%= option %>
  <% end %>

<%= f.simple_fields_for :options do |option| %>
<%= render 'option_fields', f: option %>
<% end %>

<div>
  <%= link_to_add_association 'add option', f, :options %>
</div>

<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>

views/accommodation_categories/option_fields.html.erb (2nd level)


  <%= f.input :name %>
  <%= f.check_box :_destroy %>
  <%= link_to_remove_association "remove option", f %>

<%= f.simple_fields_for :option_prices do |option_price| %>
<%= render 'option_price_fields', f: option_price %>
<% end %>


<%= link_to_add_association 'add option price', f, :option_prices %>

views/accommodation_categories/option_price_fields.html.erb

  <%= f.input :name %>
  <%= f.check_box :_destroy %>
  <%= link_to_remove_association "remove option price", f %>

The message in my terminal when sending the parameter to the controller is the following:

Processing by AccommodationCategoriesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"QlROXu9ImP6GSvPJQgd7eVZtaWsiVT6myWzZEFIEtEulSrQmt75XVMEI/avKUzhjZaZG9Kj0Pmih6J/4UYO8IQ==", "accommodation_category"=>{"options_attributes"=>{"1568290804865"=>{"name"=>"option name", "_destroy"=>"false"}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"93"}
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ /Users/xx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
  Park Load (0.4ms)  SELECT  "parks".* FROM "parks" WHERE "parks"."id" = $1 LIMIT $2  [["id", 8], ["LIMIT", 1]]
  ↳ app/controllers/accommodation_categories_controller.rb:29
  AccommodationCategory Load (0.2ms)  SELECT  "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3  [["park_id", 8], ["id", 93], ["LIMIT", 1]]
  ↳ app/controllers/accommodation_categories_controller.rb:30
   (0.2ms)  BEGIN
  ↳ app/controllers/accommodation_categories_controller.rb:32
  Option Create (0.3ms)  INSERT INTO "options" ("accommodation_category_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["accommodation_category_id", 93], ["name", "option name"], ["created_at", "2019-09-12 12:20:14.265335"], ["updated_at", "2019-09-12 12:20:14.265335"]]
  ↳ app/controllers/accommodation_categories_controller.rb:32
   (0.7ms)  COMMIT
  ↳ app/controllers/accommodation_categories_controller.rb:32
  AccommodationCategory Load (0.2ms)  SELECT  "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3  [["park_id", 8], ["id", 93], ["LIMIT", 1]]
  ↳ app/controllers/accommodation_categories_controller.rb:45
Redirected to http://localhost:3000/accommodation_categories/93/new_discounts
Completed 302 Found in 14ms (ActiveRecord: 2.3ms)


Solution

  • None of your nested-fields partials seem to have a wrapper-class? Cocoon explicitly relies on a specific mark-up, this could cause e.g. the nested fields to appear to be inserted correctly, but inserted outside of the form and then of course never posted to the server/controller.