21

I have 2 models

 class Deal < ActiveRecord::Base
   has_many :couponizations, dependent: :destroy     
   has_many :coupon_codes, through: :couponizations, source: :coupon_code, dependent: :destroy
   accepts_nested_attributes_for :coupon_codes, allow_destroy: true
 end

and

class CouponCode < ActiveRecord::Base
   has_one :couponization, dependent: :destroy
   has_one :deal, through: :couponization, source: :deal

which are linked by many-to-many relationship

class Couponization < ActiveRecord::Base
   belongs_to :coupon_code
   belongs_to :deal
end

Despite I specified dependent: :destroy option, when I delete deal, coupon codes are not being deleted. However couponizations are deleted successfully. Is there any way to delete associated nested records on object destroy?

Promise Preston
  • 24,334
  • 12
  • 145
  • 143
Ilya Cherevkov
  • 1,743
  • 2
  • 17
  • 47

2 Answers2

26

The options dependent: :destroy is ignored when using with the :through (see doc). You have to do it manually, with a after_destroy callback for example.

 class Deal

   after_destroy :destroy_coupon_codes

   private

   def destroy_coupon_codes
     self.coupon_codes.destroy_all   
   end
 end
Promise Preston
  • 24,334
  • 12
  • 145
  • 143
Baldrick
  • 23,882
  • 6
  • 74
  • 79
  • 2
    does this atomic. it means if deleting fails, delete children will be recover again. thanks. – hqt Jul 22 '17 at 13:50
  • 1
    When deleting a file attachment field (in my case, based on Refile), use destroy_all instead of delete_all; If you don't use destroy_all, the physical file might not be deleted (I was using S3, and had this issue). See discussion on difference between delete_all and destroy_all here: https://stackoverflow.com/questions/6698207/delete-all-vs-destroy-all – orberkov Oct 14 '17 at 15:10
8

I recommend using :after_destroy callback, so if destroying some Deal instance fails for whatever reason you don't end up deleting all of its CouponCodes.

Here's an :after_destroy example that should work:

after_destroy { |record|
  CouponCode.destroy(record.coupon_codes.pluck(:id))
}

Make sure to remove dependent: :destroy from has_many :couponizations in the Deals model, because all couponizations will now be destroyed by the has_one :couponization, dependent: :destroy in the CouponCode model.

Sbbs
  • 1,610
  • 3
  • 22
  • 34