通过 has_many 并加入 table 的简单形式
Simple form with has_many through and join table
我有模型:Product、Parts 和 ProductParts
product.rb
class Product < ApplicationRecord
has_many :product_parts
has_many :parts, through: :product_parts
accepts_nested_attributes_for :product_parts, allow_destroy: true
end
part.rb
class Part < ApplicationRecord
has_many :product_parts
has_many :products, through: :product_parts
end
产品_part.rb
class ProductPart < ApplicationRecord
belongs_to :part
belongs_to :product
end
产品可能由多个部分组成,例如产品 1 = 1 件第 2 部分和 3 件第 3 部分。
我加入了table:product_parts
create_table "product_parts", force: :cascade do |t|
t.integer "part_id"
t.integer "product_id"
t.integer "quantity"
t.index ["part_id"], name: "index_product_parts_on_part_id"
t.index ["product_id"], name: "index_product_parts_on_product_id"
end
我想为新产品呈现简单的形式,其中:
- 我可以输入产品名称
- select 个零件(来自所有零件)并定义每个零件数量
我有 _form.html.slim 这样的:
= simple_form_for(@product) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
= f.input :name
= f.simple_fields_for :parts do |part|
= f.label :part.name
= f.input :part.quantity
= f.button :submit
但它不呈现任何内容。您有任何正确的提示吗?
我也试过这样做:
_form.html.slim
= simple_form_for(@product) do |f|
= f.input :name
= f.simple_fields_for :parts do |part|
== render 'part_fields'
= f.button :submit
_part_fields.html.slim
= :name
= part.input :quantity
服务器日志输出为:
17:34:21 web.1 | Started GET "/products/new" for ::1 at 2020-08-24 17:34:21 +0200
17:34:21 web.1 | Processing by ProductsController#new as HTML
17:34:21 web.1 | Rendering products/new.html.slim within layouts/application
17:34:21 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | Rendered products/_part_fields.html.slim (Duration: 442.5ms | Allocations: 233999)
17:34:21 web.1 | Rendered products/_form.html.slim (Duration: 449.1ms | Allocations: 240331)
17:34:21 web.1 | Rendered products/new.html.slim within layouts/application (Duration: 451.5ms | Allocations: 242361)
17:34:21 web.1 | Completed 500 Internal Server Error in 454ms (ActiveRecord: 0.4ms | Allocations: 243658)
17:34:21 web.1 |
17:34:21 web.1 |
17:34:21 web.1 |
17:34:21 web.1 | ActionView::Template::Error - undefined local variable or method `part' for #<#<Class:0x00007f938cd03f28>:0x00007f938cd020b0>
17:34:21 web.1 | Did you mean? @parts:
17:34:21 web.1 | app/views/products/_part_fields.html.slim:2:in `view template'
谢谢大家的回答。我在 GitHub 上找到了样本:
rails-recipes. Now I am using cocoon gem 我几乎到达了我想去的地方:) 但是:
_form.html.slim
= simple_form_for(@product) do |f|
= f.input :name
|Parts
fieldset#parts
= f.simple_fields_for :product_parts do |product_part|
== render 'product_part_fields', f: product_part
= link_to_add_association 'Add part', f, :product_parts, class: 'btn btn-primary btn-xs'
= f.button :submit
_product_part.html.slim:
.nested_fields.field
= f.input :quantity
= f.collection_select :part_id, Part.all, :id, :name
= link_to_remove_association f
呈现表单时,我已经有两部分输入而不是一个(这应该是正确的)。
我不想只从列表中创建 select 个零件并仅插入数量。
它看起来像这样:
screenshot of rendered form
产品未创建,重新加载表单后我总是有 2 个部分的字段。
这是控制台日志:
12:58:14 web.1 | Started POST "/products" for ::1 at 2020-08-28 12:58:14 +0200
12:58:14 web.1 | Processing by ProductsController#create as HTML
12:58:14 web.1 | Parameters: {"authenticity_token"=>"dLVq3gUUeHnmFgVxQ7gXATOF3wdQ75a/csjmjzYN6HFrr9duoi50M4KzrSUNj/QHCrJgXO2myJPdpkybdTB0cA==", "product"=>{"name"=>"Product #1", "product_parts_attributes"=>{"0"=>{"quantity"=>"4", "part_id"=>"134", "_destroy"=>"false"}, "1"=>{"quantity"=>"5", "part_id"=>"168", "_destroy"=>"false"}, "2"=>{"quantity"=>"", "part_id"=>"266", "_destroy"=>"false"}, "3"=>{"quantity"=>"", "part_id"=>"260", "_destroy"=>"false"}, "4"=>{"quantity"=>"", "part_id"=>"262", "_destroy"=>"false"}, "5"=>{"quantity"=>"", "part_id"=>"259", "_destroy"=>"false"}}}, "commit"=>"Create Product"}
12:58:14 web.1 | (0.2ms) begin transaction
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.2ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 134], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 168], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 266], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 260], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 262], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 259], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | (0.2ms) rollback transaction
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
我做错了什么?:
- 我有两个部分可供选择,而不是新渲染形式的一个
- 无法创建产品和 products_parts
你的服务器日志的末尾显示你的部分正在寻找一个名为 'part' 的变量,并抛出一个 500 错误。
将您的 _form.html.slim
文件更改为:
= simple_form_for(@product) do |f|
= f.input :name
= f.simple_fields_for :parts do |part|
== render 'part_fields', part: part
= f.button :submit
这会将零件对象发送到零件,并清除错误。
fields_for 遍历关联并为关联中的每条记录执行一次块,但如果关联为空,则没有可迭代的内容。
因此,为了显示新记录的输入,您需要在控制器中播种关联:
class ProductsController < ApplicationController
# ...
# GET /products/new
def new
@product = Product.new do |p|
3.times { p.parts.new }
end
end
# ...
end
此外,在您第一次尝试使用符号 :part
的表单时,它会引发 NoMethodError (undefined method 'name' for :part:Symbol)
.
= f.simple_fields_for :parts do |part|
= f.label :part.name
= f.input :part.quantity
它应该是:
= f.simple_fields_for :parts do |part|
= f.label part.name
= f.input part.quantity
@marks 的回答解决了您第二次尝试中丢失的本地问题。
我弄明白了!:) 如果有人遇到同样的问题,我会把工作代码放在下面。
product.rb
class Product < ApplicationRecord
has_many :product_parts
has_many :parts, through: :product_parts
accepts_nested_attributes_for :parts, reject_if: blank?, allow_destroy: false
accepts_nested_attributes_for :product_parts, allow_destroy: true
end
product_part.rb
class ProductPart < ApplicationRecord
belongs_to :part
belongs_to :product
accepts_nested_attributes_for :part, reject_if: all_blank
end
part.rb
class Part < ApplicationRecord
has_many :product_parts
has_many :products, through: :product_parts
end
products_controller.rb
# GET /products/new
def new
@product = Product.new
@product.product_parts.build.build_part
end
products/_form.html.slim
= simple_form_for(@product) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
= f.input :name, label: false, class: 'form-control', placeholder: t('placeholders.input_product_name')
fieldset#parts
= f.simple_fields_for :product_parts do |product_part|
.field
== render 'product_part_fields', f: product_part
= link_to_add_association 'Add part', f, :product_parts, class: 'btn btn-primary btn-xs'
= f.button :submit
products/_product_part_fields.html.slim
.nested-fields.field
.field.has-addons
= link_to_remove_association f
.field
= f.input :quantity
= f.collection_select :part_id, Part.all, :id, :name, {}, {class: 'select2'}
我有模型:Product、Parts 和 ProductParts
product.rb
class Product < ApplicationRecord
has_many :product_parts
has_many :parts, through: :product_parts
accepts_nested_attributes_for :product_parts, allow_destroy: true
end
part.rb
class Part < ApplicationRecord
has_many :product_parts
has_many :products, through: :product_parts
end
产品_part.rb
class ProductPart < ApplicationRecord
belongs_to :part
belongs_to :product
end
产品可能由多个部分组成,例如产品 1 = 1 件第 2 部分和 3 件第 3 部分。
我加入了table:product_parts
create_table "product_parts", force: :cascade do |t|
t.integer "part_id"
t.integer "product_id"
t.integer "quantity"
t.index ["part_id"], name: "index_product_parts_on_part_id"
t.index ["product_id"], name: "index_product_parts_on_product_id"
end
我想为新产品呈现简单的形式,其中:
- 我可以输入产品名称
- select 个零件(来自所有零件)并定义每个零件数量
我有 _form.html.slim 这样的:
= simple_form_for(@product) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
= f.input :name
= f.simple_fields_for :parts do |part|
= f.label :part.name
= f.input :part.quantity
= f.button :submit
但它不呈现任何内容。您有任何正确的提示吗?
我也试过这样做:
_form.html.slim
= simple_form_for(@product) do |f|
= f.input :name
= f.simple_fields_for :parts do |part|
== render 'part_fields'
= f.button :submit
_part_fields.html.slim
= :name
= part.input :quantity
服务器日志输出为:
17:34:21 web.1 | Started GET "/products/new" for ::1 at 2020-08-24 17:34:21 +0200
17:34:21 web.1 | Processing by ProductsController#new as HTML
17:34:21 web.1 | Rendering products/new.html.slim within layouts/application
17:34:21 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | CACHE Part Load (0.0ms) SELECT "parts".* FROM "parts" LIMIT ? [["LIMIT", 11]]
17:34:21 web.1 | ↳ app/views/products/_form.html.slim:6
17:34:21 web.1 | Rendered products/_part_fields.html.slim (Duration: 442.5ms | Allocations: 233999)
17:34:21 web.1 | Rendered products/_form.html.slim (Duration: 449.1ms | Allocations: 240331)
17:34:21 web.1 | Rendered products/new.html.slim within layouts/application (Duration: 451.5ms | Allocations: 242361)
17:34:21 web.1 | Completed 500 Internal Server Error in 454ms (ActiveRecord: 0.4ms | Allocations: 243658)
17:34:21 web.1 |
17:34:21 web.1 |
17:34:21 web.1 |
17:34:21 web.1 | ActionView::Template::Error - undefined local variable or method `part' for #<#<Class:0x00007f938cd03f28>:0x00007f938cd020b0>
17:34:21 web.1 | Did you mean? @parts:
17:34:21 web.1 | app/views/products/_part_fields.html.slim:2:in `view template'
谢谢大家的回答。我在 GitHub 上找到了样本: rails-recipes. Now I am using cocoon gem 我几乎到达了我想去的地方:) 但是:
_form.html.slim
= simple_form_for(@product) do |f|
= f.input :name
|Parts
fieldset#parts
= f.simple_fields_for :product_parts do |product_part|
== render 'product_part_fields', f: product_part
= link_to_add_association 'Add part', f, :product_parts, class: 'btn btn-primary btn-xs'
= f.button :submit
_product_part.html.slim:
.nested_fields.field
= f.input :quantity
= f.collection_select :part_id, Part.all, :id, :name
= link_to_remove_association f
呈现表单时,我已经有两部分输入而不是一个(这应该是正确的)。 我不想只从列表中创建 select 个零件并仅插入数量。 它看起来像这样: screenshot of rendered form
产品未创建,重新加载表单后我总是有 2 个部分的字段。 这是控制台日志:
12:58:14 web.1 | Started POST "/products" for ::1 at 2020-08-28 12:58:14 +0200
12:58:14 web.1 | Processing by ProductsController#create as HTML
12:58:14 web.1 | Parameters: {"authenticity_token"=>"dLVq3gUUeHnmFgVxQ7gXATOF3wdQ75a/csjmjzYN6HFrr9duoi50M4KzrSUNj/QHCrJgXO2myJPdpkybdTB0cA==", "product"=>{"name"=>"Product #1", "product_parts_attributes"=>{"0"=>{"quantity"=>"4", "part_id"=>"134", "_destroy"=>"false"}, "1"=>{"quantity"=>"5", "part_id"=>"168", "_destroy"=>"false"}, "2"=>{"quantity"=>"", "part_id"=>"266", "_destroy"=>"false"}, "3"=>{"quantity"=>"", "part_id"=>"260", "_destroy"=>"false"}, "4"=>{"quantity"=>"", "part_id"=>"262", "_destroy"=>"false"}, "5"=>{"quantity"=>"", "part_id"=>"259", "_destroy"=>"false"}}}, "commit"=>"Create Product"}
12:58:14 web.1 | (0.2ms) begin transaction
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.2ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 134], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 168], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 266], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 260], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 262], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | Part Load (0.1ms) SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? LIMIT ? [["id", 259], ["LIMIT", 1]]
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
12:58:14 web.1 | (0.2ms) rollback transaction
12:58:14 web.1 | ↳ app/controllers/products_controller.rb:29:in `block in create'
我做错了什么?:
- 我有两个部分可供选择,而不是新渲染形式的一个
- 无法创建产品和 products_parts
你的服务器日志的末尾显示你的部分正在寻找一个名为 'part' 的变量,并抛出一个 500 错误。
将您的 _form.html.slim
文件更改为:
= simple_form_for(@product) do |f|
= f.input :name
= f.simple_fields_for :parts do |part|
== render 'part_fields', part: part
= f.button :submit
这会将零件对象发送到零件,并清除错误。
fields_for 遍历关联并为关联中的每条记录执行一次块,但如果关联为空,则没有可迭代的内容。
因此,为了显示新记录的输入,您需要在控制器中播种关联:
class ProductsController < ApplicationController
# ...
# GET /products/new
def new
@product = Product.new do |p|
3.times { p.parts.new }
end
end
# ...
end
此外,在您第一次尝试使用符号 :part
的表单时,它会引发 NoMethodError (undefined method 'name' for :part:Symbol)
.
= f.simple_fields_for :parts do |part|
= f.label :part.name
= f.input :part.quantity
它应该是:
= f.simple_fields_for :parts do |part|
= f.label part.name
= f.input part.quantity
@marks 的回答解决了您第二次尝试中丢失的本地问题。
我弄明白了!:) 如果有人遇到同样的问题,我会把工作代码放在下面。
product.rb
class Product < ApplicationRecord
has_many :product_parts
has_many :parts, through: :product_parts
accepts_nested_attributes_for :parts, reject_if: blank?, allow_destroy: false
accepts_nested_attributes_for :product_parts, allow_destroy: true
end
product_part.rb
class ProductPart < ApplicationRecord
belongs_to :part
belongs_to :product
accepts_nested_attributes_for :part, reject_if: all_blank
end
part.rb
class Part < ApplicationRecord
has_many :product_parts
has_many :products, through: :product_parts
end
products_controller.rb
# GET /products/new
def new
@product = Product.new
@product.product_parts.build.build_part
end
products/_form.html.slim
= simple_form_for(@product) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
= f.input :name, label: false, class: 'form-control', placeholder: t('placeholders.input_product_name')
fieldset#parts
= f.simple_fields_for :product_parts do |product_part|
.field
== render 'product_part_fields', f: product_part
= link_to_add_association 'Add part', f, :product_parts, class: 'btn btn-primary btn-xs'
= f.button :submit
products/_product_part_fields.html.slim
.nested-fields.field
.field.has-addons
= link_to_remove_association f
.field
= f.input :quantity
= f.collection_select :part_id, Part.all, :id, :name, {}, {class: 'select2'}