【问题标题】:Adding existing has_many records to new record with accepts_nested_attributes_for使用 accept_nested_attributes_for 将现有的 has_many 记录添加到新记录
【发布时间】:2014-06-06 20:19:40
【问题描述】:

将现有的项目模型添加到新的付款模型时,会出现错误“找不到 ID=123 的 ID 付款的项目”。这是一个 has_many 关系并使用accepts_nested_attributes_for。

class Payment < ActiveRecord::Base
  has_many :items
  accepts_nested_attributes_for :items
  ...

class Item < ActiveRecord::Base
  belongs_to :payment
  ...

付款和项目模型是假设的,但问题是真实的。在保存付款(在创建操作中完成)之前,我需要将项目与付款相关联(就像我在新操作中所做的那样)。用户需要在创建付款时修改项目的属性(例如添加帐单代码)。

具体来说,错误发生在控制器的创建动作中:

# payments_controller.rb

def new
  @payment = Payment.new
  @payment.items = Item.available # where(payment_id: nil)
end

def create
  @payment = Payment.new payment_params # <-- error happens here
  ...
end

def payment_params
  params.require(:payment).permit( ..., [items_attributes: [:id, :billcode]])
end

lib/active_record/nested_attributes.rb:543:in 'raise_nested_attributes_record_not_found!' 在调用堆栈中紧接在它之前。奇怪的是 ActiveRecord 将 payment_id 作为搜索条件的一部分。

为了完整起见,表格看起来像这样......

form_for @payment do |f|
   # payment fields ...
   fields_for :items do |i|
     # item fields    

它会在新操作上正确呈现项目。通过表单传递的参数如下所示:

{ "utf8"=>"✓",
  "authenticity_token"=>"...",
  "payment"=>{
    "items_attributes"=>{
      "0"=>{"billcode"=>"123", "id"=>"192"}
    },
  }
}

如果有更好的方法来解决这个问题而不使用accepts_nested_attributes_for,我愿意接受建议。

【问题讨论】:

  • 嘿,我认为您应该修改您的 payment_params 以允许 item_attributes 中的 :id 像这样:params.require(:payment).permit( ..., [items_attributes: [:id, :billcode]])。这样,可以从数据库中提取正确的可用项目。让我知道这个是否奏效。 `
  • 哦,你是对的。它在我的代码中只是不是这个问题。您可以在传递回来的 params 哈希中看到它。我将修复代码 sn-p。非常感谢您能理解这一点。
  • 您的payment_id 中的items 是否正确保存?
  • 它永远不会走那么远。发生错误时,付款模型仍然是新记录。
  • 看起来类似于this

标签: ruby-on-rails activerecord ruby-on-rails-4


【解决方案1】:

我只需将item_ids 集合添加到参数(除了items_attributes)即可实现此目的。您应该能够在控制器中按摩您的参数,使其看起来像这样

{ "utf8"=>"✓",
  "authenticity_token"=>"...",
  "payment"=>{
    "item_ids"=>[192]
    "items_attributes"=>{
      "0"=>{"billcode"=>"123", "id"=>"192"}
    },
  }
}

更新 1:由于某种原因,这仅适用于 item_ids 在哈希中的 items_attributes 之前。尚未审查 Rails docs 尚不清楚原因。

【讨论】:

  • 这个其实还不错。很多人发布了更复杂的解决方案。我试过了,效果很好。在表单中更新嵌套模型和渲染验证
  • 不错的技巧,如何在连接表上保存一个额外的字段,例如位置?谢谢!
  • 太棒了!这对我有用!除了在items_attributes 中使用散列,也可以使用散列数组。就像这样:items_attributes: [{ "billcode"=&gt;"123", "id"=&gt;"192 }]
【解决方案2】:

这让我很困惑......

“将现有记录添加到新记录...”

所以你有Item 1, 2, 3,并希望将它们关联到一个新的Product 对象?

--

加入模特

这样做的方法是使用join model (habtm) 而不是通过accepts_nested_attributes_for 发送数据

底线是每次您创建一个新的Product 对象时,其关联的Item 对象只能与该产品相关联:

#items table
id | product_id | information | about | item | created_at | updated_at

如果您希望使用existing Item 对象,您如何为它们定义多个 关联?事实是你不能——你必须创建一个中间表/模型,通常被引用为join model:

#app/models/product.rb
Class Product < ActiveRecord::Base
   has_and_belongs_to_many :items
end

#app/models/item.rb
Class Item < ActiveRecord::Base
   has_and_belongs_to_many :products
end

#items_products (table)
item_id | product_id

--

HABTM

如果您使用 HABTM 设置(如我在上面演示的),它将允许您从 collection 添加/删除您的各种对象,以及一个偷偷摸摸的技巧,您可以将 Items 添加到使用item_ids的产品:

#app/controllers/products_controller.rb
Class ProductsController < ApplicationController
   def create
      @product = Product.new(product_params)
      @product.save
   end

   private

   def product_params
       params.require(:product).permit(item_ids: [])
   end
end

如果您随后将参数item_ids[] 传递给您的create_method,它将为您填充collection。

如果您想将特定项目添加到 product 或删除它们,您可能希望这样做:

#app/controllers/products_controller.rb
Class ProductsController < ApplicationController
   def add
     @product = Product.find params[:id]
     @item = Item.find params[:item_id]

     @product.items << @item
     @product.items.delete params[:item_id]
   end
end

【讨论】:

  • 感谢您周到的回答。你的问题是对的。我认为您对 HABTM 的观点(我非常熟悉)是一种解决方法。在将此标记为“已接受”之前,我将再给它几天时间。不允许现有的子记录似乎是 AR 的一个缺点。作为临时修复,我将项目集合与参数分开,更新它们并单独创建付款,然后重新附加它们。
  • 我将此标记为选定答案,因为显然更改数据库架构是让 ActiveRecord 将现有子项附加到新父项的唯一方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
相关资源
最近更新 更多