我使用Rails 5.1.6并在accepts_nested_attributes_for上遇到麻烦。
我有两个模型
class Material < ApplicationRecord
belongs_to :rubric, optional: true
accepts_nested_attributes_for :rubric
end
class Rubric < ApplicationRecord
has_many :materials, dependent: :nullify
end
我尝试通过rubric_attributes设置新的项目ID。
describe 'create material' do
it 'should set rubric: :id' do
# prepare
item = FactoryBot.build(:material)
rubric = FactoryBot.create(:rubric)
# action
item.assign_attributes(
rubric_attributes: {
id: rubric.id
}
)
# check
expect(item.valid?).to eq(true)
expect(item.save).to eq(true)
expect(item.rubric_id).to eq(rubric.id)
end
end
但是我有一个错误:
Failure/Error:
item.assign_attributes(
rubric_attributes: {
id: rubric.id
}
)
ActiveRecord::RecordNotFound:
Couldn't find Rubric with ID=1 for Material with ID=1
我在更新资料时也遇到同样的错误。
accepts_nested_attributes_for是否是可预测的行为,并且我不能使用rubric_attributes设置现有的rubric ID?
答案 0 :(得分:1)
答案 1 :(得分:1)
您很可能一开始就不需要accepts_nested_attributes_for
。
如果您希望用户能够通过选择来选择记录,那么除了创建选择并将material_id
属性列入白名单之外,您实际上不需要执行任何其他操作:
<%= form_for(@material) do |f| %>
<div class="field">
<%= f.label :rubic_id %>
<%= f.collection_select :rubic_id, Rubic.all :id, :name %>
</div>
<%= f.submit %>
<% end %>
选择将在参数中创建一个数组。
class MaterialsController
# POST /materials
def create
@material = Material.new(material_params)
if @material.save
redirect_to @material
else
render :new
end
end
private
def material_params
params.require(:material)
.permit(:foo, :bar, material_ids: [])
end
end
accepts_nested_attributes_for
确实适用于需要在同一请求中创建/编辑嵌套资源的情况。您在这里使用它的唯一原因是:
您仍然可以与上面的选择一起执行1.,但是不能使用accepts_nested_attributes_for
来设置简单的belongs_to
关联。您也不想像用火箭打钉子一样。
答案 2 :(得分:1)
保留这个以防其他人可能像我一样遇到问题,通过 API 在 Rails 后端填充嵌套的子记录,但通过friendly_id 使用 hash_ids。
在尝试通过 API 修补 Rails 记录时遇到了这个问题。 第一个设置是反映 Rails 以嵌套形式发送记录值的方式。意思是,我有意构建了从前端发送到 Rails 后端的 params 哈希,就像典型的嵌套表单传输一样:
{ "children": {
"0": {
"name": "foo",
"category_id": "1",
"hash_id": "HDJPQT"
}
}
accepts_nested_attributes_for
需要 id
来修补记录。否则,它将完全创建一个新记录。在我的场景中我不想要。我发送了 hash_id
,因此无意中创建了新记录。
解决方案
暂时我不再复制嵌套的表单值哈希来发送到 Rails 后端。相反,我只是在来自 Javascript 前端的链式 fetch
查询中单独更新子记录。
注意: 如果您想继续发送一个镜像嵌套形式的哈希数组,可以根据您的需要将数据库表的主键更改为 hash_id 或 UUID。尚未测试此解决方案。