销毁和删除的区别

266

@model.destroy 和 @model.delete 有什么区别?

例如:

Model.find_by(col: "foo").destroy_all
//and
Model.find_by(col: "foo").delete_all

我使用其中一个或另一个真的很重要吗?

7个回答

357

基本上,destroy 会运行模型上的任何回调函数,而 delete 则不会。

摘自 Rails API

  • ActiveRecord::Persistence.delete

    在数据库中删除记录并冻结此实例以反映不应进行更改(因为它们无法持久化)。返回被冻结的实例。

    仅使用记录的主键上的SQL DELETE语句简单地删除行,并且不执行任何回调。

    要强制执行对象的before_destroy和after_destroy回调或任何:dependent关联选项,请使用#destroy。

  • ActiveRecord::Persistence.destroy

    在数据库中删除记录并冻结此实例以反映不应进行更改(因为它们无法持久化)。

    与destroy相关联的一系列回调。如果before_destroy回调返回false,则取消操作,并且destroy返回false。有关详细信息,请参见ActiveRecord :: Callbacks。


你好@user740584 - 感谢你的回答。 你所说的“在模型上运行任何回调”是什么意思? - BenKoshy
4
他的意思是指ActiveRecord::Callbacks(Ruby on Rails框架中的回调函数),其中之一是model#before_destroy回调函数,可以在特定条件下停止最终的destroy()调用。 - Todd
现在我知道为什么acts_as_paranoid在删除时不起作用,因为它直接通过模型进行。如果您想要软删除,请使用destroy。 - Vincent Llauderes

139

delete 只会从数据库中删除当前对象记录,而不会删除与之关联的子记录。

destroy 会从数据库中删除当前对象记录以及其关联的子记录。

它们的使用非常重要:

如果你的多个父对象共享相同的子对象,那么在特定的父对象上调用 destroy 将删除在其他多个父对象之间共享的子对象。


8
精彩的回答,谢谢。我想补充一下,据我了解这个术语是指孩子们被“杀死”,这是残忍的婴儿杀戮。 - BenKoshy
在大多数情况下,在生产中您想使用 'destroy'。 - Outside_Box
不需要,这不是必要的。 - Taimoor Changaiz
1
我认为你应该使用“后代”这个词来代替“children”,因为根据文档,destroy方法“从属性创建一个新对象,然后对其进行销毁操作。”https://www.rubydoc.info/docs/rails/4.1.7/ActiveRecord%2FRelation:destroy - Marco Lackovic

17
当您在ActiveRecord对象上调用destroydestroy_all方法时,ActiveRecord的“销毁”过程就会开始。该过程会分析您正在删除的类,并确定其相关依赖关系、运行验证等操作。
而当您在对象上调用deletedelete_all方法时,ActiveRecord只是试图对数据库运行DELETE FROM tablename WHERE conditions查询语句,并不执行其他ActiveRecord级别的任务。

7

示例:

Class User
  has_many :contents, dependent: :destroy
end

user = User.last
user.delete -> only user
user.destroy -> delete user , and contents of user

delete会从数据库中删除当前记录(无回调)

destroy会删除当前记录及其关联记录(有回调)

delete_alldestroy_all同理。


5

两种方法之间有很大的区别。如果您希望快速删除记录而不调用模型回调,请使用delete_all。

如果您关心模型回调,则使用destroy_all。

来自官方文档。

http://apidock.com/rails/ActiveRecord/Base/destroy_all/class

destroy_all(conditions = nil) 公开方法

通过实例化每个记录并调用其destroy方法来销毁与条件匹配的记录。将执行每个对象的回调(包括:dependent关联选项和before_destroy/after_destroy观察者方法)。返回已销毁的对象集合;每个对象将被冻结,以反映不应进行任何更改(因为它们无法被持久化)。

注意:当同时删除多条记录时,实例化、回调执行和每条记录的删除可能非常耗时。它会为每个记录生成至少一个SQL DELETE查询语句(或更多,以强制执行回调)。如果您想快速删除多行,而不必考虑它们的关联或回调,请改用delete_all。


4
很多答案已经提供了; 我想再补充一点。

文档:

对于has_many,destroy和destroy_all将始终调用要删除的记录的destroy方法,以便运行回调。但是,delete和delete_all将根据:dependent选项指定的策略执行删除操作,或者如果没有给出:dependent选项,则它将遵循默认策略。默认策略是什么都不做(将父ID设置为外键),除了has_many:through,其中默认策略是delete_all(删除联接记录,而不运行其回调)。

delete 命令在 ActiveRecord::Association.has_manyActiveRecord::Base 中的作用不同。对于后者,delete 将执行 SQL DELETE 并绕过所有验证/回调。前者将根据传递到关联中的 :dependent 选项执行。然而,在测试期间,我发现了以下副作用,即回调仅对 delete 而非 delete_all 运行。

dependent: :destroy 示例:

class Parent < ApplicationRecord
   has_many :children,
     before_remove: -> (_) { puts "before_remove callback" },
     dependent: :destroy
end

class Child < ApplicationRecord
   belongs_to :parent

   before_destroy -> { puts "before_destroy callback" }
end

> child.delete                            # Ran without callbacks
Child Destroy (99.6ms)  DELETE FROM "children" WHERE "children"."id" = $1  [["id", 21]]

> parent.children.delete(other_child)     # Ran with callbacks
before_remove callback
before_destroy callback
Child Destroy (0.4ms)  DELETE FROM "children" WHERE "children"."id" = $1  [["id", 22]]

> parent.children.delete_all              # Ran without callbacks
Child Destroy (1.0ms)  DELETE FROM "children" WHERE "children"."parent_id" = $1  [["parent_id", 1]]

3
基本上,“delete”直接向数据库发送查询以删除记录。在这种情况下,Rails 不知道正在删除的记录中有哪些属性,也不知道是否有任何回调(例如 before_destroy)。
“destroy”方法获取传递的 id,使用“find”方法从数据库中获取模型,然后对其进行销毁。这意味着触发了回调。
如果您不想触发回调或者想要更好的性能,则应使用“delete”。否则(大多数情况下),您将希望使用“destroy”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接