Question

I have a PurchaseOrder model and an Item model joined in a Quantity model via has_many through.

purchase_order.rb

class PurchaseOrder < ActiveRecord::Base
  has_many :quantities, dependent: :destroy
  has_many :items, :through => :quantities
  accepts_nested_attributes_for :quantities, :reject_if => :all_blank, :allow_destroy => true

item.rb

class Item < ActiveRecord::Base
  has_many :quantities, dependent: :destroy
  has_many :purchase_orders, through: :quantities
  accepts_nested_attributes_for :item, :reject_if => :all_blank

quantity.rb

class Quantity < ActiveRecord::Base
  belongs_to :purchase_order
  belongs_to :item 

The Quantity join model has an extra attribute called 'amount':

Quantity id: nil, amount: nil, purchase_order_id: nil, item_id: nil

In the new PurchaseOrder form, I want to set the amount in the Quantity join table for each new item that is created. I believe I have it almost working, but I get an error when saving the new PurchaseOrder that says Unpermitted parameters: quantity

Here is the code in my new PurchaseOrder form:

<%= f.fields_for @q do |quantity| %> 
    <%= quantity.number_field :amount %><br>
    <% end %>

I'm using rails 4, so in my PurchaseOrders controller, in the permit function I have:

def purchase_order_params
  params.require(:purchase_order).permit(:Date, :purchase_order_number, 
    :description, :quantity => [], :quantities_attributes => [], :amount, 
    :item_ids => [], :item_attributes => [], :supplier_ids => [])
end

This is the params hash that gets submitted in the form but still gives the error:

{"utf8"=>"✓", "authenticity_token"=>"DMZ6lROwRr11S3XLc2eXcb2In+G4weMZCJwhF0Bt8kQ=",
    "purchase_order"=>{"Date(1i)"=>"2013", "Date(2i)"=>"12", "Date(3i)"=>"29",
    "item_ids"=>["", "9"], "quantity"=>{"amount"=>"98"}, "supplier_ids"=>["", "4"],
    "description"=>"", "amount"=>""}, "id"=>"170"}

Why is the quantity parameter still unpermitted? Why will it not save the :amount in the Quantity join table when I submit the form? Or do I have this setup all wrong?

UPDATE Here is the full PurchaseOrder controller code: https://github.com/andrewcockerham/inventory/blob/master/app/controllers/purchase_orders_controller.rb

And here's the full sever error: (with quantity => [])

Processing by PurchaseOrdersController#update as HTML
Parameters: {"utf8"=>"✓",     "authenticity_token"=>"DMZ6lROwRr11S3XLc2eXcb2In+G4weMZCJwhF0Bt8kQ=", "purchase_order"=>   {"Date(1i)"=>"2014", "Date(2i)"=>"1", "Date(3i)"=>"1", "item_ids"=>["", "9"], "quantity"=>{"amount"=>"2345"}, "supplier_ids"=>["", "5"], "description"=>"2345", "amount"=>"2345"}, "id"=>"197"}
  PurchaseOrder Load (0.2ms)  SELECT "purchase_orders".* FROM "purchase_orders" WHERE "purchase_orders"."id" = ? LIMIT 1  [["id", "197"]]
Unpermitted parameters: quantity
   (0.1ms)  begin transaction
  Item Load (0.2ms)  SELECT "items".* FROM "items" WHERE "items"."id" = ? LIMIT 1  [["id", 9]]
  Item Load (0.1ms)  SELECT "items".* FROM "items" INNER JOIN "quantities" ON "items"."id" = "quantities"."item_id" WHERE "quantities"."purchase_order_id" = ?  [["purchase_order_id", 197]]
  SQL (0.7ms)  INSERT INTO "quantities" ("created_at", "item_id", "purchase_order_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00], ["item_id", 9], ["purchase_order_id", 197], ["updated_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00]]
  Supplier Load (0.1ms)  SELECT "suppliers".* FROM "suppliers" WHERE "suppliers"."id" = ? LIMIT 1  [["id", 5]]
  Supplier Load (0.1ms)  SELECT "suppliers".* FROM "suppliers" INNER JOIN "orders" ON "suppliers"."id" = "orders"."supplier_id" WHERE "orders"."purchase_order_id" = ?  [["purchase_order_id", 197]]
  SQL (0.3ms)  INSERT INTO "orders" ("created_at", "purchase_order_id", "supplier_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00], ["purchase_order_id", 197], ["supplier_id", 5], ["updated_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00]]
  PurchaseOrder Exists (0.1ms)  SELECT 1 AS one FROM "purchase_orders" WHERE ("purchase_orders"."purchase_order_number" = '20140101-04' AND "purchase_orders"."id" != 197) LIMIT 1
  SQL (0.2ms)  UPDATE "purchase_orders" SET "description" = ?, "amount" = ?, "Date" = ?, "updated_at" = ? WHERE "purchase_orders"."id" = 197  [["description", "2345"], ["amount", #<BigDecimal:7ff184e1e4c0,'0.2345E4',9(18)>], ["Date", Wed, 01 Jan 2014], ["updated_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00]]
   (1.1ms)  commit transaction
    `Redirected to http://localhost:3001/purchase_orders/197`
Completed 302 Found in 28ms (ActiveRecord: 3.6ms)


Started GET "/purchase_orders/197" for 127.0.0.1 at 2014-01-01 15:00:40 -0600
Processing by PurchaseOrdersController#show as HTML
  Parameters: {"id"=>"197"}
  PurchaseOrder Load (0.1ms)  SELECT "purchase_orders".* FROM "purchase_orders" WHERE "purchase_orders"."id" = ? LIMIT 1  [["id", "197"]]
DEPRECATION WARNING: This dynamic method is deprecated. Please use e.g. Post.where(...).all instead. (called from show at /Users/Andrew/code/RailsCode/TVA2/TVAInventory/app/controllers/purchase_orders_controller.rb:23)
  Quantity Load (0.2ms)  SELECT "quantities".* FROM "quantities" WHERE "quantities"."purchase_order_id" = 197
  Supplier Load (0.1ms)  SELECT "suppliers".* FROM "suppliers" INNER JOIN "orders" ON "suppliers"."id" = "orders"."supplier_id" WHERE "orders"."purchase_order_id" = ?  [["purchase_order_id", 197]]
  Item Load (0.1ms)  SELECT "items".* FROM "items" INNER JOIN "quantities" ON "items"."id" = "quantities"."item_id" WHERE "quantities"."purchase_order_id" = ?  [["purchase_order_id", 197]]
  Rendered purchase_orders/show.html.erb within layouts/application (3.5ms)
  Rendered layouts/_header.html.erb (0.6ms)
Completed 200 OK in 16ms (Views: 12.7ms | ActiveRecord: 0.5ms)

And with quantity => [:amount]

ActiveRecord::UnknownAttributeError (unknown attribute: quantity):
  app/controllers/purchase_orders_controller.rb:79:in `block in update'
  app/controllers/purchase_orders_controller.rb:78:in `update'

The entire code can be viewed in the github repo.

Was it helpful?

Solution

** Final Answer ** edited 1/1/2014
First we cut down on confusion by changing the "quantity.amount" field in the DB to "quantity.count" with a migration: Historical note, :count is a bad choice since COUNT is a SQL function

class RenameAmountInQauntities < ActiveRecord::Migration
  def change
    rename_column :quantities, :amount, :count
  end
end

run bundle exec rake db:migrate to change the table. Now you have to go into your views, etc. where you were using "amount" in reference to Quantity and change it to "count" I think the only places I really ran into it were in the app/views/quantities... files. Now onto your controller

I edited your purchase_order#new here:

  def new
  ...

  @purchase_order.purchase_order_number = @next_po_number
  # @q = @purchase_order.quantity.build ## something like this to create the Quantity record?
  @purchase_order.save
  @purchase_order.quantities.build
  # @q = Quantity.find_by_purchase_order_id(@purchase_order.id)
  @items = Item.all.order("part_number")
  @suppliers = Supplier.all.order("name")
  ...

Notice that instead of @q I'm using the .build method to make the relation that will go into the nested form. Then we make sure the form is able to accept the fields in

`...view/purchase_orders/_form.html.erb`:  
  <%= f.fields_for :quantities do |quantities| %>
      <%=  quantities.label :count %>
      <%=  quantities.text_field :count %><br>
  <% end %>

Your params look like this, note that now :count and :id are nested attributes for :quantities_attributes:

params.require(:purchase_order).permit(:Date, :purchase_order_number, :description, :amount, :count,
                                          :quantities_attributes => [:count, :id], :item_ids => [],
                                          :item_attributes => [], :supplier_ids => [])

Our form now returns:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"xilSf3CQuXoY0mnjE+jF9APXetuNv3MgMHh4JbmFzk0=", "purchase_order"=>{"Date(1i)"=>"2014", "Date(2i)"=>"1", "Date(3i)"=>"2", "item_ids"=>["", "1"], "quantities_attributes"=>{"0"=>{"count"=>"22"}}, "supplier_ids"=>["", "1"], "description"=>"whoa", "amount"=>"44"}}  

and we get a proper save. Everything seems to work great, except for some reason my .build method inserts a record into Quantities with the current purchase_order :id as it's foreign key, then instead of updating that record, it inserts another. I can't for the life of me figure out why this is happening.

SQL (0.6ms)  INSERT INTO "quantities" ("created_at", "item_id", "purchase_order_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Thu, 02 Jan 2014 06:18:01 UTC +00:00], ["item_id", 1], ["purchase_order_id", 68], ["updated_at", Thu, 02 Jan 2014 06:18:01 UTC +00:00]]
SQL (0.3ms)  INSERT INTO "quantities" ("count", "created_at", "purchase_order_id", "updated_at") VALUES (?, ?, ?, ?)  [["count", "22"], ["created_at", Thu, 02 Jan 2014 06:18:01 UTC +00:00], ["purchase_order_id", 68], ["updated_at", Thu, 02 Jan 2014 06:18:01 UTC +00:00]]

I'm throwing in the towel, I've answered pretty much every question you've brought up. I hope that you'll accept this answer and move on.

Sadly the more I poked at this application, the more things I found that bewildered me. In your new action for the PurchaseOrder controller you save the new purchase order. I can see WHY it's being done, to be able to construct the PO number out of the date and an incremented integer. But this causes you to have those horrible Create and Delete buttons at the bottom of your /purchase_order/new form. You should just have Submit and Clear buttons. You probably shouldn't be saving records before they are actually created in the #create action. You probably shouldn't be creating the PO number the way you do. And you should definitely be moving all of that business logic into methods in your models. If this is a real production app you are using, or plan to use, I would refactor most of it from the ground up after subscribing to https://www.codeschool.com/paths/ruby#rails-best-practices and watching their "Rails Best Practices" and "Rails 4 Patterns". No offense intended, I'm still learning myself, but there are some real train wrecks in this app. I would also recommend using the Cocoon Gem for nested forms. I saw this as a good exercise in learning nested forms and sharpening my Params Hash skills, and we're just sitting in front of the TV watching the first couple seasons of Downton Abbey, so I'm glad I was able to solve most of the mystery. Good luck!

End of final edit
The rest below are left as an historical reference for anyone wanting to follow the train of though used to get here.


Ok, several things seem to be wrong here. Here is my testing in the Rails console:

Loading development environment (Rails 4.0.2)
2.0.0-p353 :001 > params = ActionController::Parameters.new("purchase_order"=>{"Date(1i)"=>"2013", "Date(2i)"=>"12", "Date(3i)"=>"29", "item_ids"=>["", "9"], "quantity"=>{"amount"=>"98"}, "supplier_ids"=>["", "4"], "description"=>"", "amount"=>""}, "id"=>"170")

=> {"purchase_order"=>{"Date(1i)"=>"2013", "Date(2i)"=>"12", "Date(3i)"=>"29", "item_ids"=>["", "9"], "quantity"=>{"amount"=>"98"}, "supplier_ids"=>["", "4"], "description"=>"", "amount"=>""}, "id"=>"170"} 

2.0.0-p353 :002 > params.require(:purchase_order).permit(:Date, :purchase_order_number, :description, :quantity => [], :quantities_attributes => [], :amount, :item_ids => [], :item_attributes => [], :supplier_ids => [])
 SyntaxError: (irb):2: syntax error, unexpected ',', expecting =>
 ...ties_attributes => [], :amount, :item_ids => [], :item_attri...
 ...                               ^
from /Users/uw/.rvm/gems/ruby-2.0.0-p353/gems/railties-4.0.2/lib/rails/commands/console.rb:90:in `start'
from /Users/uw/.rvm/gems/ruby-2.0.0-p353/gems/railties-4.0.2/lib/rails/commands/console.rb:9:in `start'
from /Users/uw/.rvm/gems/ruby-2.0.0-p353/gems/railties-4.0.2/lib/rails/commands.rb:62:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'

So right off the bat I think you need to move any non-hash type declarations in the .permit section to before the ones that are using the hash syntax. I've seen this before, and am still unclear on why, but I just do it by default now. So we move :amount towards the front and we get:

2.0.0-p353 :003 > params.require(:purchase_order).permit(:Date, :purchase_order_number, :description, :amount, :quantity => [], :quantities_attributes => [], :item_ids => [], :item_attributes => [], :supplier_ids => [])
Unpermitted parameters: quantity
  => {"Date(1i)"=>"2013", "Date(2i)"=>"12", "Date(3i)"=>"29", "description"=>"", "amount"=>"", "item_ids"=>["", "9"], "supplier_ids"=>["", "4"]} 

So Rails is doing it's job and blocking what you gave it for :quantity because it's expecting a single value. So change it to :quantity => [:amount] and we see:

2.0.0-p353 :004 > params.require(:purchase_order).permit(:Date, :purchase_order_number, :description, :amount, :quantity => [:amount], :quantities_attributes => [], :item_ids => [], :item_attributes => [], :supplier_ids => [])
 => {"Date(1i)"=>"2013", "Date(2i)"=>"12", "Date(3i)"=>"29", "description"=>"", "amount"=>"", "quantity"=>{"amount"=>"98"}, "item_ids"=>["", "9"], "supplier_ids"=>["", "4"]}

Strong Parameters has now passed on your hash data happily. That solves your stated problem. Let me know if you actually get what you are looking for when you hit the controller action on submit now that the hash is getting through the whitelist. I don't see your controller code so I can't promise it will.

** EDIT **

With the updates you posted we can determine:

:quantity => []

will always give you an Unpermitted parameters: quantity , because your form is returning a hash. Either change your form to return a scalar value, or use `

:quantity => [:amount]

But I don't think you want :quantity => [:amount] since the controller update action doesn't like it. You can see when you use :quantity => [] you actually get a result, the DB does some updating and then the web server returns a #show action. So, is the DB updating correctly when you use the :quantity => [] version? You have a very complex issue well beyond your "Parameter upermitted" error. You will need to determine several things:

  1. You need to determine if the DB update that completes in your first example is doing what you want. The one thing that jumps out at me is the last commit: UPDATE "purchase_orders" SET "description" = ?, "amount" = ?, "Date" = ?, "updated_at" = ? WHERE "purchase_orders"."id" = 197 [["description", "2345"], ["amount", #], ["Date", Wed, 01 Jan 2014], ["updated_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00]] (1.1ms) commit transaction

    Does this seem right? i.e. "amount" = ? and ["amount", #] This is no doubt tied to the fact that you have "amount" in two places in your params hash (so your form is returning two different values) one as "quantity"=>{"amount"=>"98"} and the other as "amount"=>""

  2. You need look at your form that is getting rendered by the browser. This will show you how the parameters are being nested. You have a nested form that returns nested attributes. Which attributes are failing to be updated in the DB? Is this because your form is wrong or because your .permit action is set up wrong? Your DB call on update is rather complex, possibly you need to restructure the form to give you certain parameters nested in the parameters hash.

  3. Also note, your "amount" is supposed to be in the "Qauntities" table but here is your insert: INSERT INTO "quantities" ("created_at", "item_id", "purchase_order_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00], ["item_id", 9], ["purchase_order_id", 197], ["updated_at", Wed, 01 Jan 2014 21:00:40 UTC +00:00]]

    Note you are not setting an amount. Also you have "amount" in Purchase Orders and in Quantities. That is quite confusing in itself. Do they represent two different pieces of data?

As this goes beyond a simple Parameters Hash problem I would close out this question and start a new one based on "Accepts nested attributes not working in nested form and join table" or something along those lines. Also read this before crafting your next question, I think it relates to what you have going on: Rails nested form with has_many :through, how to edit attributes of join model?

Nested forms can drive me mad. I would check out this: https://github.com/nathanvda/cocoon and the Stackoverflow question that led me to it: Rails forms for has_many through association with additional attributes?

I cloned your project from Github and everything works except that you are never setting the quantity.amount for a new purchase_order. It is because your form is not passing in the correct information for the controller to be able to update the Quantities table. One thing I did to make life much easier is to rename the amount column in the Quantities table to count. You already have a column in the PurchaseOrders table called amount that represents a dollar amount. Save yourself and any future maintainers the headaches and do this to yours so that they have distinct names that represent what they are. You will also have to change any references to :amount where it pertains to the Quantities controller.

I expanded the views for the Quantities controller so that app/views/quantities/_form shows the quantity's purchase order and item name. I can call localhost:3000/quantities and then edit one of the quantity entries in the DB, giving it a :count. THAT is what is failing in your form and your create/update actions.

OTHER TIPS

Your quantity parameter is a hash, but you've only permitted it to be an array. There is no way, to my knowledge, to specify that a hash is allowed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top