Різниця між знищенням та видаленням


210

Яка різниця між

@model.destroy і @model.delete

Наприклад:

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

Невже це має значення, чи використовую я те чи інше?

Відповіді:


289

В основному destroyзапускає будь-які зворотні виклики на моделі, поки deleteнемає.

З API Rails :

  • ActiveRecord::Persistence.delete

    Видаляє запис у базі даних та заморожує цей примірник, щоб відобразити, що ніяких змін не слід вносити (оскільки їх не можна зберегти). Повертає заморожений екземпляр.

    Рядок просто видаляється оператором SQL DELETE в первинному ключі запису, і зворотні виклики не виконуються.

    Щоб застосувати зворотні виклики до_destroy та after_destroy або будь-які: залежні параметри асоціації, використовуйте #destroy.

  • ActiveRecord::Persistence.destroy

    Видаляє запис у базі даних та заморожує цей примірник, щоб відобразити, що ніяких змін не слід вносити (оскільки їх не можна зберегти).

    Існує серія зворотних викликів, пов’язаних із знищенням. Якщо зворотний виклик before_destroy повертається помилково, дія скасовується і знищується return false. Детальнішу інформацію див. У розділі ActiveRecord :: зворотні дзвінки.


Привіт @ user740584 - дякую за вашу відповідь. Що ви маєте на увазі під "запуском будь-яких зворотних викликів на моделі"?
BKSpurgeon

3
@BKSpurgeon він означає ActiveRecord :: зворотний зв'язок : api.rubyonrails.org/classes/ActiveRecord/Callbacks.html . Одним із таких зворотних дзвінків є те, model#before_destroyщо може бути застосовано для зупинки кінцевого destroy()дзвінка за певних умов.
Тодд

102

delete буде видаляти тільки поточний запис об'єкта з db, але не пов'язані з ним дочірні записи з db.

destroy видалить поточний запис об'єкта з db, а також пов'язаний з ним дочірній запис з db.

Їх використання справді має значення:

Якщо ваші декілька батьківських об'єктів мають спільні дочірні об’єкти, то виклик destroyконкретного батьківського об'єкта видалить дочірні об’єкти, які спільно використовуються серед інших кількох батьків.


5
Блискуча відповідь. спасибі. Я додам, що термінологія, наскільки я розумію, полягає в тому, що дітей "вбивають". жорстокий інфантицид.
BKSpurgeon

У більшості випадків у виробництві ви хочете використовувати "знищити"
Outside_Box

Ні в цьому немає необхідності.
Таймур Чангаїз

Я думаю, що слово, яке ви повинні використовувати, - destroyце нащадки , а не діти : згідно документації, знищуйте «створює новий об’єкт з атрибутів, а потім викликає знищення на ньому». rubydoc.info/docs/rails/4.1.7/ActiveRecord%2FRelation:destroy
Марко Лацкович

12

Коли ви викликаєте destroyабо destroy_allна ActiveRecordоб'єкті, ActiveRecordініціюється процес 'знищення', він аналізує клас, який ви видаляєте, визначає, що він повинен робити для залежностей, проходить перевірки тощо.

Коли ви викликаєте deleteабо delete_allна об'єкт, він ActiveRecordпросто намагається запустити DELETE FROM tablename WHERE conditionsзапит проти db, не виконуючи ніяких інших ActiveRecordзавдань рівня.


4

Так, є велика різниця між двома методами. Використовуйте delete_all, якщо ви хочете, щоб записи швидко видалялися без виклику зворотних викликів моделей.

Якщо ви дбаєте про зворотні дзвінки ваших моделей, тоді використовуйте kill_all

Від офіційних док

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

знищити_all (умови = нуль) громадськість

Знищує записи, що відповідають умовам, інстанціюючи кожен запис і викликаючи його метод знищення. Зворотні виклики кожного об'єкта виконуються (у тому числі: залежні параметри асоціації та методи спостерігача before_destroy / after_destroy). Повертає колекцію предметів, які були знищені; кожен буде заморожений, щоб відображати, що ніяких змін не слід вносити (оскільки їх не можна зберегти).

Примітка: миттєве завантаження, виконання зворотних дзвінків та видалення кожної записи може зайняти багато часу, коли ви видаляєте багато записів одночасно. Він генерує щонайменше один SQL DELETE-запит на запис (або, можливо, більше, щоб примусити відкликати дзвінки). Якщо ви хочете швидко видалити багато рядків, не турбуючись про їх асоціації чи зворотні дзвінки, замість цього скористайтеся delete_all.


2

В основному "delete" надсилає запит безпосередньо в базу даних, щоб видалити запис. У цьому випадку Rails не знає, які атрибути є у записі, який він видаляє, а також немає зворотних викликів (наприклад, before_destroy).

Метод "знищити" приймає переданий ідентифікатор, виймає модель з бази даних методом "знайти", а потім викликає знищення на цьому. Це означає, що зворотні виклики спрацьовують.

Ви хочете використовувати "delete", якщо ви не хочете, щоб спрацьовували зворотні дзвінки або ви хочете покращити продуктивність. В іншому випадку (і більшу частину часу) ви захочете використовувати "знищити".


2

Дуже багато відповідей; хотілося трохи більше стрибнути.

Документи :

Для has_many, знищити і kill_all завжди буде викликати метод знищення записів, які видаляються, щоб запускати зворотні виклики. Однак delete і delete_all буде або виконувати видалення відповідно до стратегії, визначеної опцією: залежно, або, якщо не вказано: залежна опція, то вона буде дотримуватися стратегії за замовчуванням. Стратегія за замовчуванням - нічого не робити (залишити закордонні ключі з встановленим батьківським ідентифікатором), за винятком has_many: through, де типовою стратегією є delete_all (видалити записи приєднання, не запускаючи їх зворотних викликів).

deleteVerbage працює по- різному для ActiveRecord::Association.has_manyі ActiveRecord::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]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.