delete_all vs destroy_all?

192

Ich suche nach dem besten Ansatz, um Datensätze aus einer Tabelle zu löschen. Zum Beispiel habe ich einen Benutzer, dessen Benutzer-ID sich über viele Tabellen erstreckt. Ich möchte diesen Benutzer und jeden Datensatz löschen, der seine ID in allen Tabellen hat.

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all
u.sources.destroy_all
u.user_stats.destroy_all
u.delete

Dies funktioniert und entfernt alle Referenzen des Benutzers aus allen Tabellen, aber ich habe gehört, dass dies destroy_allsehr prozesslastig war, also habe ich es versucht delete_all. Es entfernt nur den Benutzer aus seiner eigenen Benutzertabelle und die idaus allen anderen Tabellen werden auf null gesetzt, die darin enthaltenen Datensätze bleiben jedoch erhalten. Kann jemand mitteilen, was der richtige Prozess für die Ausführung einer solchen Aufgabe ist?

Ich sehe, dass destroy_alldie destroyFunktion für alle zugeordneten Objekte aufgerufen wird, aber ich möchte nur den richtigen Ansatz bestätigen.

glogisch
quelle

Antworten:

242

Du hast recht. Wenn Sie den Benutzer und alle zugehörigen Objekte löschen möchten -> destroy_all Wenn Sie jedoch nur den Benutzer löschen möchten, ohne alle zugeordneten Objekte zu unterdrücken ->delete_all

Laut diesem Beitrag: Rails: abhängige =>: VS zerstören: abhängig =>: delete_all

  • destroy/ destroy_all: Die zugehörigen Objekte werden neben diesem Objekt durch Aufrufen ihrer Zerstörungsmethode zerstört
  • delete/ delete_all: Alle zugehörigen Objekte werden sofort zerstört, ohne die Methode: destroy aufzurufen
Sandro Munda
quelle
80
Es sollte auch beachtet werden, dass 1) Rückrufe bei der Verwendung nicht aufgerufen werden delete_allund 2) destroy_allalle Datensätze instanziiert und einzeln zerstört werden. Bei einem sehr großen Datensatz kann dies daher schmerzhaft langsam sein.
Dylan Markow
Angenommen, ich führe eine before_destroy-Methode im Modell aus. Wenn ich delete_all verwende, wird diese Methode nicht ausgeführt. Zweitens: Wenn ich in meinem Modell eine before_delete-Methode verwende, wird diese ausgeführt, wenn ich delete oder delete_all in der Rails-Konsole ausführe?
BKSpurgeon
23

delete_all ist eine einzelne SQL DELETE-Anweisung und nichts weiter. destroy_all ruft destroy () für alle übereinstimmenden Ergebnisse von: Bedingungen (falls vorhanden) auf, bei denen es sich um mindestens NUM_OF_RESULTS SQL-Anweisungen handeln kann.

Wenn Sie etwas drastisches wie destroy_all () für große Datenmengen tun müssen, würde ich es wahrscheinlich nicht über die App tun und es manuell mit Sorgfalt behandeln. Wenn der Datensatz klein genug ist, würden Sie nicht so viel verletzen.

Ryan Her
quelle
16

Um zu vermeiden, dass destroy_allalle Datensätze instanziiert und einzeln zerstört werden, können Sie sie direkt aus der Modellklasse verwenden.

Also statt:

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all

Du kannst tun :

u = User.find_by_name('JohnBoy')
UsageIndex.destroy_all "user_id = #{u.id}"

Das Ergebnis ist eine Abfrage, um alle zugehörigen Datensätze zu zerstören

Simon-Olivier
quelle
1
Ruft es die Destroy-Rückrufe für zugeordnete Datensätze auf oder ist es UsageIndex.destroy_allgleichwertig mit UsageIntex.delete_all?
Magne
UsageIndex.destroy_allist nicht mehr verfügbar seit Schienen 3
Fabriciofreitag
1

Ich habe ein kleines Juwel gemacht , das die Notwendigkeit verringern kann, zugeordnete Datensätze unter bestimmten Umständen manuell zu löschen.

Dieses Juwel fügt eine neue Option für ActiveRecord-Zuordnungen hinzu:

abhängig :: delete_recursively

Wenn Sie einen Datensatz zerstören, werden alle Datensätze, die mit dieser Option verknüpft sind, rekursiv gelöscht (dh modellübergreifend), ohne einen von ihnen zu instanziieren.

Beachten Sie, dass diese neue Option genau wie abhängig :: löschen oder abhängig :: delete_all nicht die Rückrufe um / vor / nach_zerstörung der abhängigen Datensätze auslöst.

Es ist jedoch möglich, abhängige :: Assoziationen an einer beliebigen Stelle innerhalb einer Modellkette zu zerstören, die ansonsten mit abhängigen :: delete_recursively verknüpft sind. Die Option: destroy funktioniert normalerweise überall auf der Linie, instanziiert und zerstört alle relevanten Datensätze und löst so auch deren Rückrufe aus.

Janosch
quelle
Das ist fantastisch! Ich frage mich, warum nicht mehr Leute es auf Github gesehen / gespielt / gegabelt haben. Funktioniert es immer noch gut?
Magne
@Magne Danke! Es sollte funktionieren. Die Tests laufen auf Ruby 2.4.1 und Rails 5.1.1. Bisher habe ich es nur privat und nicht in großen Produktions-Apps verwendet, daher die Hauptversion "0", aber ich habe keine Probleme bemerkt. Es ist auch ziemlich einfach, also sollte es in Ordnung sein.
Janosch
Cool. :) Ich führe ein Projekt auf Ruby 2.3.1 und 'Rails', '~> 4.1.14' aus und bin aufgrund anderer Edelsteine ​​leider gezwungen, mich auf Activerecord (~> 4.1.0) zu verlassen. Ich sehe, dass delete_recursively in 0.9.0 aufgelöst wird. Gibt es eine ältere Version davon, die mit activerecord 4.1 funktioniert? Ich konnte keine in der Release-Registerkarte auf Github finden.
Magne
1
@Magne Ich fand, dass es tatsächlich für Activerecord ab 4.1.14 funktioniert und habe Gem Version 1.0.0 mit einer entspannten Abhängigkeit veröffentlicht. Beachten Sie jedoch, dass der 4.1-Zweig von Rails keine Sicherheitsupdates mehr erhält.
Janosch