Rails将accepts_nested_attributes_for与Emirates_to一起使用。如何删除关系而不是项目?

时间:2018-11-29 08:30:08

标签: ruby-on-rails activerecord ruby-on-rails-5

我使用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:{})

得到相同的结果

1 个答案:

答案 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]]) ...或您认为可列入白名单的任何其他字段。