【问题标题】:Rails accepts_nested_attributes_for with belongs_to. How to delete relation, not item?Rails 接受_nested_attributes_for 和belongs_to。如何删除关系,而不是项目?
【发布时间】:2018-11-29 08:30:07
【问题描述】:

我使用的是 Rails 5.1.6,但在 accept_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 销毁关系并且不从数据库中删除项目。 例如,accepts_nested_attributes_for 和 allow_destroy 与 has_many 配合得很好

附:我知道 item.update(rubric_id: nil)。但我想使用 item.update(rubric_attributes: {})

获得相同的结果

【问题讨论】:

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


    【解决方案1】:

    Rails nested_attributes(特别是在这种情况下您的 rubic_attributes 哈希)意味着您正在修改关联的记录,而不是记录本身。因为item_id 是Material 模型的一个属性,而不是关联的Rubic 模型,所以嵌套属性Hash 将无法更新该值(除非它的关联记录通过_destroy 销毁,其中Rails 似乎自动将material.rubric_id 更新为nil)。

    但是因为您不想破坏相关记录而只是通过material.update(rubric_attributes: {...}) 哈希将material.rubric_id 更新为nil,并且您不想使用简单地执行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]]) ... 或您认为可列入白名单的任何其他字段。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 2013-12-24
      • 1970-01-01
      相关资源
      最近更新 更多