错误:删除表违反外键约束。密钥 id 仍然从表中引用(很多)

Posted

技术标签:

【中文标题】错误:删除表违反外键约束。密钥 id 仍然从表中引用(很多)【英文标题】:ERROR: delete on table violates foreign key constraint. Key id is still referenced from table (many) 【发布时间】:2015-05-13 20:12:30 【问题描述】:

我正在使用 Rails 和 PostgreSQL,并且有一个基本的一对多关系,一个 Auction 有很多 Bids。但是,当我尝试删除拍卖(存在出价)时,我收到以下错误:

错误:更新或删除表“拍卖”违反外键 表“bids”上的约束“fk_rails_43e9021cbf”。详细信息:键(id)=(1) 仍然从表“出价”中引用。

删除没有出价的拍卖不会出错。

让我困惑的部分是在我的Auction 模型中,我有:

has_many :bids, dependent: :destroy

既然我有一个依赖的destroy子句,为什么我还是会收到这个错误?

编辑:我已尝试删除整个数据库,然后重新创建/重新迁移所有内容 - 仍然出现相同的错误。

【问题讨论】:

【参考方案1】:

您的错误来自数据库而不是 rails。您需要先在您的应用中删除出价或更改数据库中的外键约束以级联删除

【讨论】:

我试过“rake db:drop”然后重新创建数据库并重新运行迁移文件......仍然得到同样的错误! 如果里面有一个外键,那么我们所做的就是删除它并重新创建它。您需要搜索脚本,看看它是否在那里,检查数据库本身。 我也有这个问题,看到是DB报错了,但是为什么rails不先删除依赖记录呢? OP 和我有模型约束 dependent: :destroy.【参考方案2】:

您是否使用deletedestroy 来删除对象?我认为您正在使用delete,并且您想使用destroy

见http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Delete+or+destroy-3F

【讨论】:

谢谢!切换到销毁而不是删除解决了问题!【参考方案3】:

Marc Busqué 有 a very good article 关于这个问题可能会有所帮助。

“当 ActiveRecord 遇到外键违规时,它会引发 ActiveRecord::InvalidForeignKey 异常。即使在其文档中只是说当记录无法插入或更新时引发它,因为它引用了不存在的记录,事实上,它也用于我们感兴趣的案例中。”

有了它和一个rescue_from,我们就可以添加到ApplicationController或控制器关注点:

rescue_from 'ActiveRecord::InvalidForeignKey' do
  # Flash and render, render API json error... whatever
end

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效【参考方案4】:

您是否偶然使用了paranoia gem 或类似的东西?

如果你是bidsparanoidauctions 不是,你可能会遇到这个错误。

这会发生,因为当 rails 执行 dependent: destroy 时,它会软删除出价,但它们实际上仍然存在于数据库中(它们只有 deleted_at 列集)。因此,外键约束会失败。

【讨论】:

不,没有使用 gems,请参阅我的更新解决方案/解释【参考方案5】:

我的问题是我在尝试删除记录时使用了@auction.delete(在我发布的屏幕截图中可见)。

Delete 将忽略我已有的任何回调。所以即使我有一个从属的destroy子句,它也没有被调用——因此Rails会抛出一个错误。如果/当我将代码更改为 @auction.destroy 时,回调被调用并解决了问题。

参考: Difference between Destroy and Delete

【讨论】:

好地方!这是我的确切问题!【参考方案6】:

Rails v4.2 开始,您可以这样做:

创建迁移以更新外键

20160321165946_update_foreign_key.rb

class UpdateForeignKey < ActiveRecord::Migration
  def change
    # remove the old foreign_key
    remove_foreign_key :posts, :users

    # add the new foreign_key
    add_foreign_key :posts, :users, on_delete: :cascade
  end
end

【讨论】:

谢谢,我修正了那个错字。 如何在初始参考创建迁移中执行此操作?例如,我是否这样做:ActiveRecord::Migration[5.0] def change add_reference :job_queries, :user, foreign_key: true, on_delete: :cascade end end (从 Rails 5.1.4 的角度来看)我觉得 foreign_key 是数据库级别的设置,而dependent: :delete_all 是模型级别的设置。如果我们在数据库中设置了 foreign_key(检查您的 schema.rb),dependent: :delete_all 是不够的,我们还需要数据库级别的 on_delete: :cascade。我发现这篇文章很有帮助:spin.atomicobject.com/2016/10/04/…. 这是一个很好的解决方案,并且为我解决了这个问题......但是......当我们通过 @ 告诉 Rails 这样做时,为什么我们必须求助于清理数据库987654325@?我以前在其他代码库上没有见过这种情况,但在我目前的代码库中,我不得不使用大量的这些!【参考方案7】:

其他答案都不错,但别提有时你想留下依赖记录,但要使外键无效

class Post < ActiveRecord::Base
  has_many :comments, dependent: :nullify
end

请注意,这需要确保数据库表中的外键列具有 null: true

我不肯定,但您可能还需要将 optional: true 添加到依赖模型中定义的属于关联。

【讨论】:

【参考方案8】:

一个非常简单的解释:关联表包含至少 1 条记录,该记录连接到您要销毁的表中的记录。

要解决此问题,请添加 dependent: :destroy(假设用户拥有多个帖子)

has_many :post, dependent: :destroy

【讨论】:

以上是关于错误:删除表违反外键约束。密钥 id 仍然从表中引用(很多)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SAP ASE Sybase 16 中从具有外键的表中删除行

MySql主从表的主表删除数据

从表 2 中的外键约束中删除表 1 中确实具有主键的记录

答辩6

在 PostgreSQL 中使用外键删除行

完整性错误:更新或删除违反外键约束。 Django + PostgreSQL