【问题标题】:Deleting rails active records with delete_all/delete violates foreign key constraint使用 delete_all/delete 删除 rails 活动记录违反外键约束
【发布时间】:2020-01-31 10:16:36
【问题描述】:

我有一个带有 :dependent => :destroy 的活动记录关联设置,它按预期工作。 然后我发现由于性能原因我需要使用delete而不是destroy,所以我只是根据关联将destroy更改为delete_all/delete。

当我尝试删除时:

shop.shop_snapshots.completed.last.delete

我收到错误消息:

ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR:  update or delete on table "shop_snapshots" violates foreign key constraint "fk_rails_c24b24adaf" on table "inventory_items"

但是为什么会这样 - 我相信我在快照上进行了正确的设置:

has_many :inventory_items, :dependent => :delete_all

它对破坏有效,所以我做错了什么?

谢谢 /路易丝

【问题讨论】:

  • 这能回答你的问题吗? Difference between Destroy and Delete
  • 不完全-它说“删除只会从数据库中删除当前对象记录,但不会从数据库中删除其关联的子记录”。这意味着只有我的快照被删除,而不是关联的inventory_items,这解释了错误消息。但是为什么 Rails 支持设置 :dependent => :delete_all,如果它不支持对子级的级联删除 - 那么“:dependent => :delete_all”将不起作用?
  • 如果你打电话给destroy而不是delete,我相信它会起作用
  • 是的,使用destroy 已经按照我的描述工作了——但我不想使用destroy,因为我需要delete 提供的更好的性能。
  • 请再次阅读第一条评论中链接的文本:只有destroy 考虑了:dependent 关联操作。 delete 只能提供更好的性能因为它跳过回调和依赖关联检查。

标签: ruby-on-rails activerecord associations cascading-deletes


【解决方案1】:

在 Postgres 上,您可以在外键本身上使用 CASCADE 选项。

CASCADE 指定当引用的行被删除时,行 引用它也应该被自动删除。
- https://www.postgresql.org/docs/9.5/ddl-constraints.html

这通常在创建表时设置,但您可以通过删除然后重新添加外键约束将其添加到现有表中:

class AddCascadeToOrderItems < ActiveRecord::Migration[6.0]
  def up
    remove_foreign_key :order_items, :orders
    add_foreign_key :order_items, :orders, on_delete: :cascade
  end

  def down
    remove_foreign_key :order_items, :orders
    add_foreign_key :order_items, :orders
  end
end

由于这是在数据库级别处理的,因此您的模型中不需要配置。

has_many :inventory_items, dependent: :delete_all

同样有效,并且是 MySQL 等农民数据库的唯一选项,但只有当您在声明关联作为模型回调实现的模型上调用 .destroy 而不是 .delete 时才会触发它。例如:

class Store < ApplicationRecord
  has_many :inventory_items, dependent: :delete_all
end

store = Store.find(1)
store.destroy # triggers callbacks and will delete all assocatiated inventory_items
store.delete  # will not trigger callbacks

【讨论】:

  • 好的,谢谢,您的回答对我帮助很大。几个后续问题:a)在 down 方法中,remove 和 add 方法应该切换对吗? b) 好的,我看到依赖项: :delete_all 如果我使用销毁启动它,它可以工作,但它只在“一个级别”上工作,即任何子 order_items 可能不会被删除,因为回调不是由删除触发的order_items 正如你所解释的那样。出于这个原因,我需要在外键上使用级联。
  • A) 是的,你是对的 - 顺序是错误的。 B) 是的。也对。使用 .delete_all 方法删除关联记录,该方法仅创建单个 SQL 删除查询,不实例化模型或调用回调。
  • 这种情况下级联的优点是级联,因此如果您正确设置外键,子孙将被删除。
【解决方案2】:

您需要在迁移级别进行设置。

类似这样的:

create_table :childs do |t|
  t.references :parent, index: true, foreign_key: {on_delete: :cascade}
  t.string :name

  t.timestamps null: false
end

【讨论】:

    猜你喜欢
    • 2021-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多