使用accepts_nested_attributes_for将现有has_many记录添加到新记录

将现有项模型添加到新的付款模型时,会出现错误“无法找到ID = 123的项目,ID = 123”。 这是一个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!' 紧接在callstack之前。 很奇怪ActiveRecord包含payment_id作为搜索条件的一部分。

为了完整,表单看起来像这样……

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

并在新操作上正确呈现Items。 通过表单的params看起来像这样:

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

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

这困惑了我……

“将现有记录添加到新记录中……”

那么你有Item 1, 2, 3 ,并希望将它们与一个新的Product对象相关联?

加入模型

这样做的方法是使用join modelhabtm )而不是通过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添加/删除,以及一个偷偷摸摸的技巧,您可以使用item_idsItems添加到产品:

 #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 

如果您随后将param item_ids[]传递给item_ids[] ,它将为您填充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 

我通过向params添加item_ids集合(除了items_attributes )来实现这items_attributes 。 您应该能够在控制器中按下您的参数,看起来像这样

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

更新1:由于某种原因,只有当item_idsitems_attributes中的item_ids之前items_attributes 。 尚未审查Rails文档尚未找出原因。