ActiveRecord:为什么 has_many dependent: :destroy 不工作?
ActiveRecord: Why is has_many dependent: :destroy not working?
出于某种原因,我在销毁记录后得到 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 时,噩梦发生了
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
我在销毁 id=348 的 vacacion 时尝试调试 ActiveRecord,我发现了这个
# 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" =
# but the error still happens
else
delete_all
end
另外,这个问题只出现在vacacion_id
FK上,并且只出现在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,该记录创建了另一个将原始 vacacion 与 vacation_transaction 相关联的记录,因此在 ActiveRecord 尝试删除原始假期时,最后由于关联模型中我的 after_destroy 代码,仍然被引用。
当然,我从未想过这一点,这就是为什么在我最初的问题中我什至懒得分享我的模型的回调。
解决方案是更改我的回调和关联的顺序,因为 Rails 执行它们的顺序与您在模型中编写它们的顺序相同
出于某种原因,我在销毁记录后得到 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 时,噩梦发生了
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
我在销毁 id=348 的 vacacion 时尝试调试 ActiveRecord,我发现了这个
# 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" =
# but the error still happens
else
delete_all
end
另外,这个问题只出现在vacacion_id
FK上,并且只出现在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,该记录创建了另一个将原始 vacacion 与 vacation_transaction 相关联的记录,因此在 ActiveRecord 尝试删除原始假期时,最后由于关联模型中我的 after_destroy 代码,仍然被引用。
当然,我从未想过这一点,这就是为什么在我最初的问题中我什至懒得分享我的模型的回调。
解决方案是更改我的回调和关联的顺序,因为 Rails 执行它们的顺序与您在模型中编写它们的顺序相同