ActiveRecord:为啥 has_many 依赖: :destroy 不起作用?

Posted

技术标签:

【中文标题】ActiveRecord:为啥 has_many 依赖: :destroy 不起作用?【英文标题】:ActiveRecord: Why is has_many dependent: :destroy not working?ActiveRecord:为什么 has_many 依赖: :destroy 不起作用? 【发布时间】:2021-12-03 22:38:07 【问题描述】:

由于某种原因,我在销毁记录后收到了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

这里有一个例子: vacacion id=348,vacacion_percibida id=950 和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" = $1
  # 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

那么,我错过了什么?

谢谢。

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

我不认为这是我所缺少的。我在其他型号上已经有其他has_many,问题只发生在这个has_many :vacation_transactions 上。我尝试了这条额外的线路以防万一,但问题仍然存在。

以上是关于ActiveRecord:为啥 has_many 依赖: :destroy 不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

Rails ActiveRecord:三个表 has_many 通过:关联

Rails ActiveRecord 关联“has_many,每个都有 has_one”

如何使用has_many通过关系缩短三重连接ActiveRecord查询?

带有 has_many belongs_to 关联的 Rails activerecord 查询

ActiveRecord :inverse_of 不适用于 has_many :through 在创建时的连接模型上

如何通过“has_many”关联获取数据?