ActiveRecord:为什么has_many依赖::destroy不工作?



由于某种原因,我在销毁记录后得到PG::ForeignKeyViolation: ERROR

这里是迁移

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

这里是模型

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

这里有一个示例:id=348的vacacion, id=950的vacacion_percibida和

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

但是当我试图破坏id=348的假期时,噩梦发生了

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

我尝试调试ActiveRecord,同时销毁休假id=348,我发现这个

# 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

同样,这个问题只发生在vacacion_idFK中,并且只发生在Vacacion的少量记录中

我使用ruby 2.7.4p191, Rails 6.0.4.1, ActiveRecord 6.0.4.1

那么,我错过了什么?

谢谢。

我认为您需要修改您的假期模型并添加accepts_nested_attributes_for

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

所以,我试图删除FK并再次添加它,但问题仍然存在。在最后一次尝试中,我删除并再次添加了FK,但指定了级联参数add_foreign_key :vacation_transactions, :vacacions, on_delete: :cascade,现在问题解决了。

我仍然不明白为什么我必须在这个特定的FK中指定这个,以及这个解决方案是否会在将来引发另一个问题。

编辑:经过很长一段时间,我意识到我有dependent: :destroy和一些after_save回调的问题。vacacion的销毁触发了另一个关联记录的销毁,该记录具有一个after_destroy,该after_destroy创建了另一个将原始vacacion与vacation_transaction关联的记录,因此,在ActiveRecord试图删除原始假期时,由于关联模型中的我的after_destroy代码,它仍然被引用。

当然,我从来没有想过这一点,这就是为什么在我最初的问题中,我甚至没有费心分享我的模特的反馈。

解决方案是改变我的回调和关联的顺序,因为Rails执行它们的顺序与你在模型中编写它们的顺序相同