我有几个表具有与它们相关联的外键约束,每个表以层次结构的方式引用另一个表,如下所示。
当我试图摧毁至少有一个项目,至少有一个任务,至少有一个TaskTime这样的公司.
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
发布于 2018-02-12 05:46:33
来自精细手册
has_many(name,作用域=零,选项= {},和扩展) ..。
: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
,这将在销毁它们之前将所有内容从数据库中提取出来,回调将被调用(这将从数据库中加载更多的东西,但只会销毁它们,从而从数据库中加载更多的东西……)。最终的结果将是大量的数据库活动,但是所有的外键都会被正确地遵循。
或者,您可以通过指定论外键约束将逻辑放入通常属于它的数据库中。
级联指定,当被引用的行被删除时,也应该自动删除引用它的行。
您的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
留在您的模型中,以提醒您发生了什么,或者您可以给自己留下评论。
发布于 2020-09-02 00:07:53
我也遇到了同样的问题,只需先销毁关联表中的记录,然后销毁主表,就可以完成我所需要的工作。这样,它没有违反参照的完整性,因此没有错误。
我从这个问题那里得到了这个答案
发布于 2021-08-13 09:36:46
我也有同样的问题,我用
rails db:reset
https://stackoverflow.com/questions/48739646
复制相似问题