【问题标题】:Nested form input for join table attributes in a has_many through association, parameter is unpermitted通过关联在 has_many 中连接表属性的嵌套表单输入,不允许使用参数
【发布时间】:2014-01-16 15:45:01
【问题描述】:

我有一个 PurchaseOrder 模型和一个 Item 模型通过 has_many 加入到一个 Quantity 模型中。

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

数量.rb

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

Quantity 连接模型有一个称为“金额”的额外属性:

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

在新的 PurchaseOrder 表单中,我想在 Quantity 连接表中为创建的每个新项目设置金额。我相信我几乎可以使用它,但是在保存新的 PurchaseOrder 时出现错误,上面写着Unpermitted parameters: quantity

这是我的新 PurchaseOrder 表单中的代码:

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

我使用的是 rails 4,所以在我的 PurchaseOrders 控制器中,在 permit 函数中我有:

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

这是在表单中提交但仍然给出错误的参数哈希:

{"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"}

为什么数量参数仍然不允许?为什么我提交表单时它不会将 :amount 保存在 Quantity 连接表中?还是我的设置全错了?

更新 这是完整的 PurchaseOrder 控制器代码: https://github.com/andrewcockerham/inventory/blob/master/app/controllers/purchase_orders_controller.rb

这是完整的服务器错误:(quantity =&gt; []

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)

还有quantity =&gt; [: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'

整个代码可以在 github repo 中查看。

【问题讨论】:

  • 在您的白名单中,您有:quantity =&gt; [], :quantities_attributes =&gt; [], :amount,:amountQuantity 以外的表中的字段吗?您是否尝试过从白名单中删除 :quantity =&gt; [] 以查看会发生什么?其他两个模型中的列是什么?当您提交表单并遇到错误时,您能否显示整个 Web 服务器日志?
  • 我在想:quantity =&gt; [:amount]quantities_attributes =&gt; [:amount] 可能是您要找的东西?也试试这些,让我知道。
  • 我尝试了这两种方法,但我从purchaseorder#update 操作中得到了未知属性错误。我已经更新了帖子以包含控制器代码和完整的错误代码
  • 我在两个不同的浏览器中重新加载了这个页面,并没有看到新的错误代码或控制器代码。
  • 啊抱歉,我还在打字:)

标签: ruby-on-rails ruby-on-rails-4 nested-forms nested-attributes has-many-through


【解决方案1】:

** 最终答案 ** 2014 年 1 月 1 日编辑
首先,我们通过迁移将数据库中的“quantity.amount”字段更改为“quantity.count”来减少混淆:历史记录,:count 是一个糟糕的选择,因为 COUNT 是一个 SQL 函数

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

运行bundle exec rake db:migrate 来更改表格。现在你必须进入你的观点,等等,你在引用数量时使用“数量”并将其更改为“计数”我认为我真正遇到的唯一地方是在 app/views/quantities... 文件中。现在到你的控制器上

我在这里编辑了你的purchase_order#new

  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")
  ...

请注意,我使用.build 方法代替@q 来创建将进入嵌套表单的关系。然后我们确保表单能够接受

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

您的参数如下所示,注意现在:count:id:quantities_attributes 的嵌套属性:

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

我们的表单现在返回:

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"}}  

我们得到了正确的保存。一切似乎都很好,除了出于某种原因我的.build 方法将一条记录插入到 Quantities 中,当前purchase_order :id 作为它的外键,然后不是更新该记录,而是插入另一个记录。我一生都无法弄清楚为什么会这样。

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]]

我认输了,我已经回答了你提出的几乎所有问题。我希望你能接受这个答案并继续前进。

可悲的是,我对这个应用程序戳得越多,我发现让我感到困惑的事情就越多。在 PurchaseOrder 控制器的 new 操作中,您保存新的采购订单。我可以看到为什么要这样做,以便能够根据日期和递增的整数构造 PO 编号。但这会导致您在/purchase_order/new 表单的底部出现那些可怕的CreateDelete 按钮。您应该只有提交和清除按钮。在#create 操作中实际创建记录之前,您可能不应该保存记录。您可能不应该以您的方式创建采购订单号。而且您绝对应该将所有业务逻辑转移到模型中的方法中。如果这是您正在使用或计划使用的真实生产应用程序,我会在订阅https://www.codeschool.com/paths/ruby#rails-best-practices 并观看他们的“Rails 最佳实践”和“Rails 4 模式”之后从头开始重构其中的大部分。无意冒犯,我仍在学习自己,但这个应用程序中有一些真正的火车残骸。我还建议将Cocoon Gem 用于嵌套表单。我认为这是学习嵌套形式和提高我的 Params Hash 技能的一个很好的练习,我们只是坐在电视机前观看《唐顿庄园》的前几季,所以我很高兴我能够解决大部分问题谜。祝你好运!

最终编辑结束
下面的其余部分留作历史参考,供任何想要追随曾经到达这里的火车的人使用。


好的,这里似乎有几件事是错误的。这是我在 Rails 控制台中的测试:

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>'

因此,我认为您需要将 .permit 部分中的所有非散列类型声明移到使用散列语法的声明之前。我以前见过这个,但仍然不清楚为什么,但我现在只是默认这样做。所以我们将:amount 移到前面,我们得到:

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"]} 

所以 Rails 正在做它的工作并阻止你为:quantity 提供的东西,因为它期待一个单一的价值。所以把它改成:quantity =&gt; [:amount],我们看到了:

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 参数现在已经愉快地传递了您的哈希数据。这解决了你所说的问题。让我知道当您在提交时点击控制器操作时您是否真的得到了您正在寻找的东西,因为哈希正在通过白名单。我没有看到你的控制器代码,所以我不能保证它会。

** 编辑 **

根据您发布的更新,我们可以确定:

:quantity => []

总会给你一个 Unpermitted parameters: quantity ,因为你的表单会返回一个哈希值。更改表单以返回标量值,或使用 `

:quantity => [:amount]

但我认为您不需要 :quantity =&gt; [:amount],因为控制器更新操作不喜欢它。您可以看到,当您使用 :quantity =&gt; [] 时,您实际上得到了一个结果,数据库进行了一些更新,然后 Web 服务器返回了一个 #show 操作。那么,当您使用:quantity =&gt; [] 版本时,数据库是否正确更新?您有一个非常复杂的问题,远远超出了“参数允许”错误。您需要确定几件事:

  1. 您需要确定在您的第一个示例中完成的数据库更新是否正在执行您想要的操作。让我印象深刻的一件事是最后一次提交: 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

    这看起来对吗?即"amount" = ?["amount", #] 这无疑与您在参数哈希中的两个位置有“数量”(因此您的表单返回两个不同的值)一个为"quantity"=&gt;{"amount"=&gt;"98"} 而另一个为@987654364 @

  2. 您需要查看浏览器呈现的表单。这将向您展示参数是如何嵌套的。您有一个返回嵌套属性的嵌套表单。哪些属性无法在数据库中更新?这是因为您的表单错误还是因为您的 .permit 操作设置错误?您对更新的数据库调用相当复杂,可能您需要重组表单以提供嵌套在参数哈希中的某些参数。

  3. 另请注意,您的“金额”应该在“数量”表中,但这是您的插入: 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]]

    请注意,您没有设置金额。此外,您在采购订单和数量中有“金额”。这本身就很令人困惑。它们代表两个不同的数据吗?

由于这超出了简单的参数哈希问题,我将结束这个问题,并根据“接受嵌套属性不能在嵌套形式和连接表中工作”或类似的内容开始一个新问题。在制定下一个问题之前,请阅读此内容,我认为这与您正在做的事情有关:Rails nested form with has_many :through, how to edit attributes of join model?

嵌套表单会让我发疯。我会看看这个:https://github.com/nathanvda/cocoon 和引发我的 Stackoverflow 问题:Rails forms for has_many through association with additional attributes?

我从 Github 克隆了您的项目,一切正常,只是您从未为新的 purchase_order 设置 quantity.amount。这是因为您的表单没有传递正确的信息,以便控制器能够更新 Quantities 表。为了让生活更轻松,我做的一件事是将 Quantities 表中的 amount 列重命名为 count。您已经在 PurchaseOrders 表中有一个名为 amount 的列,它表示美元金额。为您自己和任何未来的维护者省去麻烦,对您的维护者这样做,以便他们有不同的名称来代表他们的身份。您还必须更改与 Quantities 控制器相关的对 :amount 的任何引用。

我扩展了 Quantities 控制器的视图,以便 app/views/quantities/_form 显示数量的采购订单和项目名称。我可以调用localhost:3000/quantities,然后编辑数据库中的一个数量条目,给它一个:count。这就是您的表单和创建/更新操作失败的原因。

【讨论】:

  • 我更新了我的帖子以显示update 函数。我更新了.permit 函数以与您的函数相同,然后出现此错误: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'
  • 我在重新加载页面时没有看到任何变化,您正在编辑它吗?我已经解决了你的参数问题。您现在看到的是与您的控制器代码和可能您的表单代码相关的完全不同的问题。我建议您根据新问题创建一个新问题。您需要发布整个控制器和表单代码,以及 Web 服务器的输出,而不仅仅是错误。这里重要的是要记住,除非您发布它,否则我看不到任何内容。 Web 服务器日志可以包含大量有用的数据来告诉您问题所在。
  • 这可能不是一个巨大的错误,但看到所有代码会更容易解决。这是在 Github 上吗?可以上传到 Github 吗?
  • 是的,刚刚用链接更新了帖子。非常感谢你的帮助!但是现在出现“未知属性”错误......所以你说它与另一个问题有关吗?
  • 我按照您的建议将Quantities 表中的:amount 列更改为:count。但当然它不会改变未经允许的参数错误。我同意我可以去localhost:3000/quantities并编辑:count列没有问题。但是你是对的,问题是,我怎样才能设置新的 PurchaseOrder 表单以便我可以从那里输入现在的:count 列(quantity.count)?我可以提出一个新问题,但这个问题并没有真正解决,因为我仍然得到未经允许的参数错误。我会尝试你链接到的其他问题。非常感谢你的帮助!
【解决方案2】:

您的quantity 参数是一个散列,但您只允许它是一个数组。据我所知,没有办法指定允许使用哈希。

【讨论】:

  • 是的,我认为这可能存在一些问题。那么如何在新的 PurchaseOrder 表单中设置 Quantity 连接模型的 :amount 列呢?
  • 抱歉,我得跑了,不知道什么时候才能恢复。
  • :quantity =&gt; [:amount] 可以很好地返回 {:amount =&gt; "some amount"} 的哈希值。你真正遇到问题的地方是当你想要传递一个哈希并且你事先不知道密钥是什么时。然后你必须对.tap 做一些诡计,如本示例末尾附近所示:blog.sensible.io/2013/08/17/strong-parameters-by-example.html
猜你喜欢
  • 1970-01-01
  • 2014-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-21
  • 2016-03-01
  • 1970-01-01
相关资源
最近更新 更多