Home > Net >  ActiveRecord: Why is has_many dependent: :destroy not working?
ActiveRecord: Why is has_many dependent: :destroy not working?

Time:10-19

For some reason, I'm getting PG::ForeignKeyViolation: ERROR after destroying a record.

Here I have the migration

  create_table :vacation_transactions do |t|
  t.belongs_to  :vacacion, index: true, foreign_key: true, null: true
  t.references  :vacacion_percibida, index: true, foreign_key: true, null: true
  t.timestamps
end

And here I have the models

class Vacacion < ApplicationRecord
  has_many :vacation_transactions, dependent: :destroy
end

class VacacionPercibida < ApplicationRecord
  has_many   :vacation_transactions, dependent: :nullify
end

class VacationTransaction < ApplicationRecord
  belongs_to :vacacion, optional: true
  belongs_to :vacacion_percibida, optional: true
end

Here I have an example: vacacion with id=348, a vacacion_percibida with id=950 and a vacation_transaction with

  #<VacationTransaction:0x00007f390901cc48> {
                           :id => 20,
                  :vacacion_id => 348,
        :vacacion_percibida_id => 950,
                   :created_at => some_date,
                   :updated_at => some_date
    }

But when I try to destroy the vacacion with id=348 the nightmare happens

  Vacacion.find(348).destroy! 
  # PG::ForeignKeyViolation: ERROR:  update or delete on table "vacacions" violates foreign key constraint "fk_rails_ae595e109b"
  # on table "vacation_transactions" DETAIL:  Key (id)=(348) is still referenced from table "vacation_transactions"

  # if I do the next lines I get the same error
  VacationTransaction.find(20).destroy! # cool
  VacationTransaction.find(20) # ActiveRecord::RecordNotFound, that means the record is destroyed
  Vacacion.find(348).destroy! # Same PG::ForeignKeyViolation: ERROR

I tried debugging ActiveRecord while destroying the vacacion with id=348 and I found this

# lib/active_record/associations/has_many_association.rb
when :destroy
  load_target.each { |t| t.destroyed_by_association = reflection }
  # load_target actually has the vacation_transaction record to destroy
  destroy_all
  # it actually destroys the vacation_transaction, in the console I can see the DELETE FROM "vacaciones_percibidas" WHERE "vacaciones_percibidas"."id" = $1
  # but the error still happens
else
  delete_all
end

Also, this problem only happens with the vacacion_id FK and only in a small amount of records of Vacacion

I'm using ruby 2.7.4p191, Rails 6.0.4.1, ActiveRecord 6.0.4.1

So, what am I missing?

Thanks.

CodePudding user response:

I think you need to modify your vacation model and add accepts_nested_attributes_for

class Vacacion < ApplicationRecord
  has_many :vacation_transactions, dependent: :destroy
  accepts_nested_attributes_for :vacation_transactions, allow_destroy: true
end

CodePudding user response:

So, I tried to remove the FK and adding it again and the problem persisted. In a last attempt I removed and added again the FK but specifying the cascade parameter add_foreign_key :vacation_transactions, :vacacions, on_delete: :cascade and now the problem is solved.

I still don't why I have to specify this in this specific FK and if this solution is going to trigger another problems in the future.

  • Related