0

So I'm building an HABTM relationship and I always get this error back in the terminal when I submit the form:

Unpermitted parameter: :color_ids

No other errors. The app works fine, except that the associations are always an empty array.

Schema.rb:

create_table "colors", force: :cascade do |t|
  t.string "color"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "colors_products", id: false, force: :cascade do |t|
  t.integer "color_id", null: false
  t.integer "product_id", null: false
end

create_table "products", force: :cascade do |t|
  t.string "title"
  t.decimal "price"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

color.rb:

class Color < ApplicationRecord
  has_and_belongs_to_many :products
end

product.rb:

class Product < ApplicationRecord
  has_and_belongs_to_many :colors
end

_form.html.erb:

<div class="field">
  <%= form.label :color %>
  <%= form.collection_select  :color_ids, Color.all, :id, :color, {:multiple => true}, class: "input-field-data" %>
</div>

product_controller.rb

def product_params
  params.require(:product).permit(:title, :price, color_ids: [])
end

changing the param hash from color ids to color_ids:[:id, :color] makes no difference.

This example is obviously just something I recreated to see if I did something else wrong in my original app, probably also easier to debug.

Any ideas what's wrong with that setup? I actually have another project with the exact same setup and it works? Because of that, I think that I'm missing something, but I don't actually find anything wrong with my code.

Thanks in advance for any input!

Edit:

As requested, here is the terminal log when submitting a new product with a collection_select as shown above:

Started POST "/products" for 127.0.0.1 at 2019-02-10 14:02:59 +0100
Processing by ProductsController#create as HTML
  Parameters: {"authenticity_token"=>"+f+GJaN58M029eGICvMqlwtjYB4Qmv/KNBY0OnymrxyFy+zNYXKfZtCXR0NM3kLY16QIzfLb+takhNjgIQXeEw==", "product"=>{"title"=>"abc", "price"=>"9.99", "color_ids"=>"1"}, "commit"=>"Create Product"}
Unpermitted parameter: :color_ids
   (0.1ms)  begin transaction
  ↳ app/controllers/products_controller.rb:30:in `block in create'
  Product Create (1.0ms)  INSERT INTO "products" ("title", "price", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["title", "abc"], ["price", 9.99], ["created_at", "2019-02-10 13:02:59.634965"], ["updated_at", "2019-02-10 13:02:59.634965"]]
  ↳ app/controllers/products_controller.rb:30:in `block in create'
   (1.1ms)  commit transaction
  ↳ app/controllers/products_controller.rb:30:in `block in create'
Redirected to http://localhost:3000/products/15
Completed 302 Found in 14ms (ActiveRecord: 2.3ms | Allocations: 3885)


Started GET "/products/15" for 127.0.0.1 at 2019-02-10 14:02:59 +0100
Processing by ProductsController#show as HTML
  Parameters: {"id"=>"15"}
  Product Load (0.4ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 15], ["LIMIT", 1]]
  ↳ app/controllers/products_controller.rb:67:in `set_product'
  Rendering products/show.html.erb within layouts/application
  Rendered products/show.html.erb within layouts/application (Duration: 1.1ms | Allocations: 302)
Completed 200 OK in 23ms (Views: 16.0ms | ActiveRecord: 0.4ms | Allocations: 8945)

Also:

Submitting via rails console works fine, so this has definitely something to do with the form i guess:

irb(main):010:0> p = Product.last

=> #<Product id: 15, title: "abc", price: 0.999e1, created_at: "2019-02-10 13:02:59", updated_at: "2019-02-10 13:02:59">

irb(main):011:0> p.colors

=> #<ActiveRecord::Associations::CollectionProxy []>

irb(main):012:0> p.colors << [Color.last]

=> #<ActiveRecord::Associations::CollectionProxy [#<Color id: 2, col: "Red", created_at: "2019-02-10 09:04:42", updated_at: "2019-02-10 09:04:42">]>

irb(main):013:0> p.colors

=> #<ActiveRecord::Associations::CollectionProxy [#<Color id: 2, col: "Red", created_at: "2019-02-10 09:04:42", updated_at: "2019-02-10 09:04:42">]>

_form.html.erb (generated by scaffold and adjusted with the collection_select field)

<%= form_with(model: product, local: true) do |form| %>
  <% if product.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(product.errors.count, "error") %> prohibited this product from being saved:</h2>

      <ul>
      <% product.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>

  <div class="field">
    <%= form.label :price %>
    <%= form.text_field :price %>
  </div>

  <div class="field">
    <%= form.label :color_ids %>
    <%= form.collection_select( :color_ids,  Color.all, :id, :col, {multiple: true}) %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

Greetings!

Prometheus
  • 799
  • 8
  • 28
  • Have you tried using `accepts_nested_attributes_for :colors` in product.rb? [This answer](https://stackoverflow.com/questions/39046532/unpermitted-parameter-in-rails-5/39046713) might help. – Jake Feb 09 '19 at 13:43
  • can you show your server's log when you do the request? – arieljuod Feb 09 '19 at 15:23
  • @arieljuod sure, i've updated the question with the log. – Prometheus Feb 10 '19 at 13:05

1 Answers1

1

You are expecting an array/hash but you are receiving a single string. It looks like the helper is not creating a multiple select, do you see the multiple="true" attr on the select tag? If not, try changing the method to this:

form.collection_select( :color_ids,  Color.all, :id, :col, {}, {multiple: true})

Note the extra {}. The helper expect the first hash to be the options for the helper and the second hash to be the options for the tag.

arieljuod
  • 15,460
  • 2
  • 25
  • 36
  • The only question i have is: The error comes back when i add multiple: false or remove multiple completely. Any idea why that is so? – Prometheus Feb 10 '19 at 17:09
  • The `color_ids` setter expects an array of ids, if you remove the `multiple="true"` attribute from the select then you'll receive just one color id as a string and not an array. I don't see why would you have a HABTM relationship if you don't use a multiple select though (it would be more likely a belongs_to relationship). If you can elaborate a little more on why you don't want the select to be multiple maybe I can suggest something better. – arieljuod Feb 10 '19 at 17:26
  • Well, the idea was to add later on for each color also a quantity column, so that a product( a tshirt for example) can have multiple colors with different quantities. Think of a shop owner who wants to add his stock and he currently has 3 white shirts and 2 blue shirts of the same shirt. In this case, a product has many colors and colors many products. But because of the quantity, each color must be also seperated. So my original thought was to add multiple collection select to the form so that the shop owner can add multiple colors separately, which would make multiple true unnecessary. – Prometheus Feb 10 '19 at 19:26
  • 1
    If you need to add extra fields for the assocaition, you'll need to use a "has_many :through" association and a join model to save both ids and the quantity (check the guide https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many). The common approach to that is to use nested forms (`fields_for` method on forms) with activerecords' `accept_nested_attributes_for` https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html – arieljuod Feb 10 '19 at 19:33
  • Alright, i knew about :through associations but didn't knew i need them in this case. I'll check that out! Thanks for your help! – Prometheus Feb 10 '19 at 19:38