Rails принимает_nested_attributes_for с принадлежащим.Как удалить отношение, а не элемент? - PullRequest
0 голосов
/ 29 ноября 2018

Я использую 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 и неудалить элемент из базы данных.Например, acceptpts_nested_attributes_for с allow_destroy хорошо работает с has_many

PS Я знаю о item.update (rubric_id: nil).Но я хочу получить тот же результат, используя item.update (rubric_attributes: {})

1 Ответ

0 голосов
/ 29 ноября 2018

Rails nested_attributes (в частности, для этого случая ваш rubic_attributes хэш) означает, что вы изменяете связанную запись (записи), а не саму запись.Поскольку item_id является атрибутом модели материала, а не связанной модели Rubic, то вложенный атрибут Hash не сможет обновить это значение (если только связанная с ним запись не уничтожена через _destroy, из которых, похоже, Railsавтоматически обновлять material.rubric_id до nil).

Но поскольку вы не хотите уничтожать связанную запись, а просто обновляете material.rubric_id до nil через material.update(rubric_attributes: {...}) Hash и вам это не нужночтобы использовать «обычный способ» простого выполнения 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]]) ... или любые другие поля, которые вы считаете белыми.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...