在嵌套表单的父参数上使用子模型访问器 (STI)
Using submodel accessor (STI) on parent params for nested forms
我有以下型号:
class Order < ApplicationRecord
class Bill < Order
class Preorder < Order
class Detail < ApplicationRecord
class Metric < Detail
class Unit < Detail
模型 Order
接受 Detail
的嵌套属性,Order
可以是 Bill
或 Preorder
,Detail
可以由 Unit
或 Metric
我正在使用 jsonb_accessor gem 为 Unit
和 Metric
创建附加属性
class Metric < Detail
jsonb_accessor :data,
height: :decimal,
width: :decimal
end
class Unit < Detail
jsonb_accessor :data,
quantity: :decimal
end
在我看来,我正在使用 Cocoon gem 为 Unit
或 Metric
生成字段
<%= link_to_add_association(I18n.t('activerecord.models.metric.add'), f, :details, data: {association_insertion_node: '#details', association_insertion_method: :append}, :class => "btn btn-success",
wrap_object: Proc.new { |detail| detail.type = "Metric"; detail = Metric.new }) %>
<%= link_to_add_association(I18n.t('activerecord.models.unit.add'), f, :details, data: {association_insertion_node: '#details', association_insertion_method: :append}, :class => "btn btn-success",
wrap_object: Proc.new { |detail| detail.type = "Unit"; detail = Unit.new }) %>
在我的控制器中,我有这个方法来验证参数
def order_params(type)
if type=="Bill"
params.require(:bill).permit(:client_id, :type, :delivered, :paid, :total_paid, details_attributes: [:id, :total_price, :type, :data, :service_id, :_destroy])
elsif type=="Preorder"
params.require(:preorder).permit(:client_id, :type, :delivered, details_attributes: [:id, :total_price, :type, :data, :service_id, :_destroy])
end
end
表单几乎完美地工作,它存储了我在表单中输入的所有数据,除了 :width, :height, :quantity
,当我将它们放入参数时,它给出了一个错误,指出这些参数不属于细节模型并且当我改用 :data
时,该列仍然是空的。
我正在考虑,直接为父模型使用列 Detail
而不是使用 jsonb_accessor,或者为每个 Metric
和 Unit
模型单独使用视图和控制器但是我保留这些解决方案作为最后的手段,因为我想保留代码 Dryier 并尽可能使用更少的列
恕我直言,问题是您正在使用关联 details
,这意味着 rails 正在尝试 create/save 一个 Detail
。相反,您必须使用 units
和 metrics
的相应关联,以便 rails 知道创建具有相应 jsonb_accessors
的正确对象才能正确保存。
您可以在模型中添加关联:
has_many :metrics
has_many :units
我知道这在您的 UI 中可能不那么干净(如果一切都是细节,您可以 add/list 将它们放在一起)。另一种选择是在 Detail
模型中定义所有可能的 json-fields(但不完全确定这是否违背了首先使用它们的目的)。
我有以下型号:
class Order < ApplicationRecord
class Bill < Order
class Preorder < Order
class Detail < ApplicationRecord
class Metric < Detail
class Unit < Detail
模型 Order
接受 Detail
的嵌套属性,Order
可以是 Bill
或 Preorder
,Detail
可以由 Unit
或 Metric
我正在使用 jsonb_accessor gem 为 Unit
和 Metric
class Metric < Detail
jsonb_accessor :data,
height: :decimal,
width: :decimal
end
class Unit < Detail
jsonb_accessor :data,
quantity: :decimal
end
在我看来,我正在使用 Cocoon gem 为 Unit
或 Metric
<%= link_to_add_association(I18n.t('activerecord.models.metric.add'), f, :details, data: {association_insertion_node: '#details', association_insertion_method: :append}, :class => "btn btn-success",
wrap_object: Proc.new { |detail| detail.type = "Metric"; detail = Metric.new }) %>
<%= link_to_add_association(I18n.t('activerecord.models.unit.add'), f, :details, data: {association_insertion_node: '#details', association_insertion_method: :append}, :class => "btn btn-success",
wrap_object: Proc.new { |detail| detail.type = "Unit"; detail = Unit.new }) %>
在我的控制器中,我有这个方法来验证参数
def order_params(type)
if type=="Bill"
params.require(:bill).permit(:client_id, :type, :delivered, :paid, :total_paid, details_attributes: [:id, :total_price, :type, :data, :service_id, :_destroy])
elsif type=="Preorder"
params.require(:preorder).permit(:client_id, :type, :delivered, details_attributes: [:id, :total_price, :type, :data, :service_id, :_destroy])
end
end
表单几乎完美地工作,它存储了我在表单中输入的所有数据,除了 :width, :height, :quantity
,当我将它们放入参数时,它给出了一个错误,指出这些参数不属于细节模型并且当我改用 :data
时,该列仍然是空的。
我正在考虑,直接为父模型使用列 Detail
而不是使用 jsonb_accessor,或者为每个 Metric
和 Unit
模型单独使用视图和控制器但是我保留这些解决方案作为最后的手段,因为我想保留代码 Dryier 并尽可能使用更少的列
恕我直言,问题是您正在使用关联 details
,这意味着 rails 正在尝试 create/save 一个 Detail
。相反,您必须使用 units
和 metrics
的相应关联,以便 rails 知道创建具有相应 jsonb_accessors
的正确对象才能正确保存。
您可以在模型中添加关联:
has_many :metrics
has_many :units
我知道这在您的 UI 中可能不那么干净(如果一切都是细节,您可以 add/list 将它们放在一起)。另一种选择是在 Detail
模型中定义所有可能的 json-fields(但不完全确定这是否违背了首先使用它们的目的)。