我使用Rails 5.1.6并在accepts_nested_attributes_for上遇到麻烦。
我有两个模型
class Material < ApplicationRecord
belongs_to :rubric, optional: true
accepts_nested_attributes_for :rubric, allow_destroy: true
end
class Rubric < ApplicationRecord
has_many :materials, dependent: :nullify
end
我不知道如何破坏材料和专栏之间的关系。
我有一个测试用例:
it 'should delete relation to rubric' do
# create two materials with relation to the same rubric
item = FactoryBot.create(:material_with_rubric)
expect(Rubric.count).to eq(1)
expect(item.rubric_id).to_not eq(nil)
FactoryBot.create(:material, rubric_id: item.rubric_id)
expect(Material.count).to eq(2)
# try to destroy relation for first material
item.assign_attributes(
rubric_attributes: {
id: item.rubric_id,
_destroy: '1'
}
)
# check
expect(item.valid?).to eq(true)
expect(item.save).to eq(true)
expect(item.rubric_id).to eq(nil)
# rubric should exist in database because we have second material with relation
expect(Rubric.count).to be > 0
end
但是,在运行测试用例之后,我看到一个错误:
Failure/Error: expect(Rubric.count).to be > 0
expected: > 0
got: 0
如何使用rubric_attributes破坏关系并且不从数据库中删除该项目。 例如,带有allow_destroy的accepts_nested_attributes_for与has_many配合得很好
P.S。我知道item.update(rubric_id:nil)。但是我想使用item.update(rubric_attributes:{})
得到相同的结果答案 0 :(得分:2)
滑行nested_attributes
(在这种情况下,尤其是rubic_attributes
哈希)意味着您正在修改关联的记录,而不是记录本身。由于item_id
是Material模型的属性,而不是关联的Rubic
模型的属性,因此嵌套属性Hash将无法更新该值(除非其关联的记录已通过_destroy
销毁了) ,其中Rails似乎会自动将material.rubric_id
更新为nil
)。
但是因为您不想破坏关联的记录,而只是通过material.rubric_id
哈希将nil
更新为material.update(rubric_attributes: {...})
,并且您不想使用“正常方式”简单地进行material.update(rubric_id: nil)
的操作,然后您可以执行以下变通方法,仍然使用rubric_attributes
:
class Material < ApplicationRecord
belongs_to :rubric, optional: true
accepts_nested_attributes_for :rubric, allow_destroy: true
end
class Rubric < ApplicationRecord
has_many :materials, dependent: :nullify
accepts_nested_attributes_for :materials
end
rubric = Rubric.create!
material = Material.create!(rubric: rubric)
material.update!(
rubric_attributes: {
id: material.rubric.id,
materials_attributes: {
id: material.id,
rubric_id: nil # or `rubric: nil`
}
}
)
puts material.rubric_id
# => 1
# not sure why `material` needs to be reloaded. Rails `inverse_of` seems to not work on "nested" nested_attributes
material.reload
puts material.rubric_id
# => nil
puts Rubric.exists?(id: 1)
# => true
# now you've updated material.rubric_id to nil, but not destroying the Rubric record
重要!如果出于安全目的,将在控制器中使用此嵌套属性,请不要忘记只允许params.permit(rubric_attributes: [materials_attributes: [:rubric_id]])
...或您认为可列入白名单的任何其他字段。