TL;DR
class Harbor
has_many :ships
accepts_nested_attributes_for :ships, allow_destroy: true
end
harbor.ships_attributes = [harbor.ships.first.attributes.merge('_destroy': true)]
harbor.save!
harbor.ships_destroyed_during_save? # true
See that last line of code? That is what this gem adds
The long version
On more than one occasion, I have found myself needing to know if a collection had an object that was destroyed via assignment of nested attributes. Unfortunately, I don’t believe ActiveRecord has a way do to that.
The root of the issue, as far as I have understood, is that the destroyed objects are removed from the associated collection. Because of that, I only know of two ways to check if there was a destruction during the save.
- Store the ids of the items in the collection before saving, and check if any of them do not exist after saving.
- Add a
before_save
callback that stores a list of items that are marked for destruction. After the save is committed, the list can be used to know if any items were destroyed.
Since there is a lot going on to properly implement #2, I decided to write a gem to handle it.
The gem is available at GitHub - brandoncc/activerecord-nested_attribute_destruction. I hope others find it useful, and I would love to receive any feedback that the community has.
Here is an example of usage:
class Lighthouse < ActiveRecord::Base
belongs_to :harbor
end
class Ship < ActiveRecord::Base
belongs_to :harbor
end
class Harbor < ActiveRecord::Base
has_many :ships
has_one :lighthouse
accepts_nested_attributes_for :ships, :lighthouse, allow_destroy: true
end
harbor = Harbor.create(name: 'Blue Lagoon')
harbor.ships << Ship.new
harbor.lighthouse = Lighthouse.new
harbor.save
harbor.ships_attributes = [harbor.ships.first.attributes.merge('_destroy': true)]
harbor.save
puts harbor.ships_destroyed_during_save?
# => true
puts harbor.lighthouse_destroyed_during_save?
# => false
harbor.lighthouse_attributes = harbor.lighthouse.attributes.merge('_destroy': true)
harbor.save
puts harbor.ships_destroyed_during_save?
# => false
puts harbor.lighthouse_destroyed_during_save?
# => true
Thanks for taking the time to read this. Happy holidays!