我有几个表具有与之关联的外键约束,每个表以分层方式引用另一个,如下所述。
当我试图摧毁一个至少有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"
答案 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那里得到了这个答案