首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PG::ForeignKeyViolation: ERROR:更新或删除表"xxx“违反外键约束

PG::ForeignKeyViolation: ERROR:更新或删除表"xxx“违反外键约束
EN

Stack Overflow用户
提问于 2018-02-12 04:41:24
回答 3查看 19.9K关注 0票数 8

我有几个表具有与它们相关联的外键约束,每个表以层次结构的方式引用另一个表,如下所示。

当我试图摧毁至少有一个项目,至少有一个任务,至少有一个TaskTime这样的公司.

代码语言:javascript
运行
复制
irb(main):014:0> Company.first.destroy

我得到下面的输出和错误。我现在的印象是,简单地让dependent: :delete_all不处理外键约束,这是真的吗?如果是的话,我该如何处理这种情况?我知道before_destroy回调,在这种情况下我必须使用它吗?如果是的话,我如何暂时禁用外键约束,以便销毁行中所有相关的行?让这更令人困惑的是,我有一个旧的rails项目,它只设置了相同的表/模型--它是一个带有外键约束的model_a has_many model_bs, dependent: delete_all关系,我可以ModelB.destroy_all,它可以工作,所以我不理解它。我还读过在表上设置级联删除的文章,有些帖子说,如果您自己用代码处理它,就不需要这样做;如果解决方案不太复杂,我想在代码中处理这个问题。

代码语言:javascript
运行
复制
Company Load (0.4ms)  SELECT  "companies".* FROM "companies" ORDER BY 
                              "companies"."id" ASC LIMIT $1 [["LIMIT", 1]]
   (0.2ms)  BEGIN
             SQL (0.9ms)  DELETE FROM "projects" 
                          WHERE "projects"."company_id" = $1 [["company_id", 3]]
   (0.1ms)  ROLLBACK
             Traceback (most recent call last):
   1: from (irb):13
             ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR:  update or delete on table "projects" violates foreign key constraint "fk_rails_02e851e3b7" on table "tasks"
                          DETAIL:  Key (id)=(4) is still referenced from table "tasks".
                        : DELETE FROM "projects" WHERE "projects"."company_id" = $1)

模式

代码语言:javascript
运行
复制
# /db/schema.rb

create_table "companies", force: :cascade do |t|
...
end

create_table "projects", force: :cascade do |t|
...
end

create_table "tasks", force: :cascade do |t|
...
end

create_table "task_times", force: :cascade do |t|
...
end
...

add_foreign_key "projects", "companies"
add_foreign_key "tasks", "projects"
add_foreign_key "task_times", "tasks"

模型

代码语言:javascript
运行
复制
# /models/company.rb

class Company < ApplicationRecord
  has_many :projects, dependent: :delete_all
...
end

# /models/project.rb

class Project < ApplicationRecord
  has_many :tasks, dependent: :delete_all
...
end

# /models/task.rb

class Task < ApplicationRecord
  has_many :task_times, dependent: :delete_all
...
end

# /models/task_time.rb

class TaskTime < ApplicationRecord
...
end
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-02-12 05:46:33

来自精细手册

has_many(name,作用域=零,选项= {},和扩展) ..。

  • :dependent 控制关联对象在其所有者被销毁时发生的情况。请注意,这些是作为回调实现的,Rails按照顺序执行回调。因此,其他类似的回调可能会影响:dependent行为,而:dependent行为可能会影响其他回调。
    • :destroy会导致所有相关的对象也被销毁。
    • :delete_all会直接从数据库中删除所有相关的对象(因此不会执行回调)。
    • ..。

因此,:delete_all确实负责外键,但是,由于没有调用回调,所以它只能深入一个级别。所以这在Company

代码语言:javascript
运行
复制
has_many :projects, dependent: :delete_all

这意味着调用公司的#destroy将直接从数据库中删除关联的projects。但这是看不见的:

代码语言:javascript
运行
复制
has_many :tasks, dependent: :delete_all

您在Project中具有的,并最终尝试删除仍在tasks中引用的项目,如错误消息所示。

您可以将所有关联切换到dependent: :destroy,这将在销毁它们之前将所有内容从数据库中提取出来,回调将被调用(这将从数据库中加载更多的东西,但只会销毁它们,从而从数据库中加载更多的东西……)。最终的结果将是大量的数据库活动,但是所有的外键都会被正确地遵循。

或者,您可以通过指定论外键约束将逻辑放入通常属于它的数据库中。

级联指定,当被引用的行被删除时,也应该自动删除引用它的行。

您的add_foreign_key调用看起来应该是:

代码语言:javascript
运行
复制
add_foreign_key "projects", "companies", on_delete: :cascade
add_foreign_key "tasks", "projects", on_delete: :cascade
add_foreign_key "task_times", "tasks", on_delete: :cascade

在这种情况下。您可能希望将dependent: :delete_all留在您的模型中,以提醒您发生了什么,或者您可以给自己留下评论。

票数 21
EN

Stack Overflow用户

发布于 2020-09-02 00:07:53

我也遇到了同样的问题,只需先销毁关联表中的记录,然后销毁主表,就可以完成我所需要的工作。这样,它没有违反参照的完整性,因此没有错误。

我从这个问题那里得到了这个答案

票数 0
EN

Stack Overflow用户

发布于 2021-08-13 09:36:46

我也有同样的问题,我用

代码语言:javascript
运行
复制
rails db:reset
票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48739646

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档