PG :: ForeignKeyViolation:错误:更新或删除表" xxx"违反外键约束

时间:2018-02-12 04:41:25

标签: ruby-on-rails postgresql ruby-on-rails-5 rails-activerecord

我有几个表具有与之关联的外键约束,每个表以分层方式引用另一个,如下所述。

当我试图摧毁一个至少有1个Project的公司时,该公司至少有1个Task,其中至少有1个TaskTime ......

irb(main):014:0> Company.first.destroy

我得到以下输出和错误。我现在的印象是,只是dependent: :delete_all没有处理外键约束,这是真的吗?如果是这样,我该如何处理这种情况?我知道before_destroy回调,在这种情况下我是否必须使用它?如果是这样,我如何暂时禁用外键约束以销毁所有相关的行?更令人困惑的是,我有一个旧的rails项目,只有与外键约束的单model_a has_many model_bs, dependent: delete_all关系设置相同的表/模型,我可以{{ 1}}它有效,所以我没有得到它。我还阅读了在桌面上设置级联删除的帖子和一些帖子说如果你自己在代码中处理它就不需要这样做;如果解决方案不太毛茸茸,我想在我的代码中处理这个问题。

ModelB.destroy_all

模式

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)

模型

# /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"

2 个答案:

答案 0 :(得分:6)

来自fine manual

  

has_many (name,scope = nil,options = {},& extension)
  [...]

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

所以:delete_all会处理外键,但由于没有调用回调,因此它只会深入一级。所以这在Company

has_many :projects, dependent: :delete_all

表示在公司上调用#destroy将直接从数据库中删除关联的projects。但那不会看到这个:

has_many :tasks, dependent: :delete_all

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

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

或者,您可以通过指定on delete cascade on the foreign key constraints

将逻辑放在数据库中通常所属的位置
  

CASCADE指定当删除引用的行时,引用它的行也应自动删除

您的add_foreign_key电话看起来像是:

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留在模型中以提醒您发生了什么,或者您可以留言。

答案 1 :(得分:0)

我遇到了同样的问题,并通过简单地先破坏关联表中的记录,然后然后破坏主表来实现了我所需要的。这样就不会违反参照完整性,因此不会出现错误。

我从this question那里得到了这个答案

相关问题