通过关联,has_many中的连接表属性的嵌套表单输入是不允许的

时间:2013-12-29 17:23:21

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

我有一个PurchaseOrder模型和一个通过has_many通过数量模型加入的Item模型。

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 

数量连接模型有一个名为'amount'的额外属性:

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

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

以下是我的新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

这是在表单中提交但仍然提供错误的params哈希:

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

为什么数量参数仍未被允许?为什么我提交表单时不会在数量连接表中保存:金额?或者我的设置都错了吗?

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

这是完整的服务器错误:(使用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)

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

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

2 个答案:

答案 0 :(得分:3)

**最终答案** 2014年1月1日编辑
首先,我们通过改变&#34; quantity.amount&#34;来减少混乱。数据库中的字段为&#34; quantity.count&#34;迁移:历史记录,:由于COUNT是SQL函数,因此计数是一个糟糕的选择

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

运行bundle exec rake db:migrate来更改表格。现在你必须进入你所使用的观点等等#34;金额&#34;参考数量并将其更改为&#34; count&#34;我认为我真正遇到的唯一地方是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")
  ...

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

中的字段
`...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方法将记录插入到数量中并使用当前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编号和递增的整数。但是这会导致您在Create表单底部放置那些可怕的Delete/purchase_order/new按钮。你应该只有提交和清除按钮。在#create操作中实际创建记录之前,您可能不应该保存记录。您可能不应该像您那样创建采购订单编号。您绝对应该将所有业务逻辑转移到模型中的方法中。如果这是您正在使用或计划使用的真实生产应用程序,我会在订阅https://www.codeschool.com/paths/ruby#rails-best-practices并观看他们的&#34; Rails最佳实践&#34;之后从头开始重构其中的大部分内容。和&#34; Rails 4模式&#34;。没有违法行为,我还在学习自己,但这个应用程序中有一些真正的火车残骸。我还建议使用Cocoon Gem嵌套表单。我认为这是学习嵌套形式和提高我的Params Hash技能的一个很好的练习,我们只是坐在电视机前观看Downton Abbey的前几个季节,所以我很高兴我能够解决大部分的谜团。祝你好运!

最终修改结束
下面的其余部分留给任何想要跟随这里的火车的人的历史参考。


好的,这里似乎有些错误。这是我在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 => [: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"]}

强参数现在已经愉快地传递了哈希数据。这解决了你陈述的问题。让我知道,如果你现在通过白名单获取哈希,那么当你点击提交控制器动作时,你实际上得到了你想要的东西。我没有看到您的控制器代码,因此我无法承诺。

**编辑**

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

:quantity => []

将始终为您提供Unpermitted parameters: quantity,因为您的表单正在返回哈希值。更改表单以返回标量值,或使用“

:quantity => [:amount]

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

  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", #]毫无疑问,这与你有&#34;金额&#34;你的params散列中的两个地方(所以你的表单返回两个不同的值)一个作为"quantity"=>{"amount"=>"98"},另一个作为"amount"=>""

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

  3. 另请注意,您的&#34;金额&#34;应该是在&#34; Qauntities&#34;表但这里是你的插入: 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]]

    请注意,您没有设置金额。你也有&#34;金额&#34;在采购订单和数量。这本身就很混乱。它们代表两种不同的数据吗?

  4. 由于这超出了一个简单的参数哈希问题,我将结束这个问题并开始一个新的基于&#34;接受嵌套形式的嵌套属性和连接表&#34;或类似的规定。在制作下一个问题之前,请先阅读此内容,我认为这与您的情况有关: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之外,一切正常。这是因为您的表单没有传递正确的信息,控制器无法更新数量表。我为使生活更轻松而做的一件事是将“数量”表中的amount列重命名为count。您已在PurchaseOrders表中有一个名为amount的列,表示美元金额。保护自己和任何未来的维护者头痛并为您做到这一点,以便他们有不同的名称,代表他们是什么。您还必须更改任何对以下内容的引用:与数量控制器相关的数量。

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

答案 1 :(得分:0)

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