我有几个表,它们都有关联的外键约束,每个表都以分层方式引用另一个表,如下所述。
当我试图摧毁一个公司,至少有1个项目,至少有1个任务,至少有1个任务时间像这样...
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
,它工作正常。所以我不明白,我也读过一些帖子,在表上设置级联删除是有效的,还有一些帖子说如果你自己用代码处理它,就不需要这样做;如果解决方案不太复杂的话,我想在我的代码中处理这个问题。
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"
型
型号
# /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
3条答案
按热度按时间y0u0uwnf1#
从精细手册:
has_many(名称,作用域= nil,选项= {},扩展名&)
[...]
:dependent
控制当相关对象的所有者被销毁时,它们会发生什么。注意,这些都是作为回调实现的,并且Rails会按顺序执行回调。因此,其他类似的回调可能会影响
:dependent
行为,而:dependent
行为可能会影响其他回调。:destroy
导致所有关联的对象也被销毁。:delete_all
导致所有关联的对象直接从数据库中删除(因此将不执行回调)。所以
:delete_all
确实处理了外键,但是由于没有调用回调函数,所以它只处理了一个层次,在Company
中是这样的:意味着调用某个公司的
#destroy
将直接从数据库中删除关联的projects
,但不会看到以下内容:您在
Project
中拥有的项目,并且您最终尝试删除仍在tasks
中引用的项目,正如错误消息所指示的那样。您可以将所有关联切换到
dependent: :destroy
,这将在销毁它们之前从数据库中取出所有内容,并调用回调(这将从数据库中加载更多内容,但销毁它们将从数据库中加载更多内容...)。最终结果将是大量数据库活动,但所有外键都将得到正确遵循。或者,您可以通过在外键约束上指定
on delete cascade
,将逻辑放入数据库中它通常所属的位置:CASCADE指定删除引用行时,引用该行的行也应自动删除
您的
add_foreign_key
调用如下所示:型
在这种情况下,您可能希望在您的模型中留下
dependent: :delete_all
作为正在发生的事情的提醒,或者您可以给自己留下评论。6pp0gazn2#
我也遇到了同样的问题,我只需要先销毁关联表中的记录,然后再销毁主表,就完成了我所需要的工作,这样就不会违反引用完整性,因此也就不会出现错误。
我从this question得到了这个答案
hgb9j2n63#
我也有同样的问题,我用