delete_all vs destroy_all?

192

Szukam najlepszego podejścia do usuwania rekordów z tabeli. Na przykład mam użytkownika, którego identyfikator użytkownika znajduje się w wielu tabelach. Chcę usunąć tego użytkownika i każdy rekord, który ma swój identyfikator we wszystkich tabelach.

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

To działa i usuwa wszystkie referencje użytkownika ze wszystkich tabel, ale słyszałem, że destroy_allbyło to bardzo obciążające proces, więc spróbowałem delete_all. Usuwa tylko użytkownika ze swojej własnej tabeli użytkowników, a idwszystkie pozostałe tabele są zerowane, ale pozostawiają w nich nienaruszone rekordy. Czy ktoś może udostępnić prawidłowy proces wykonywania takiego zadania?

Widzę, że destroy_allwywołuje destroyfunkcję na wszystkich powiązanych obiektach, ale chcę tylko potwierdzić prawidłowe podejście.

glogiczny
źródło

Odpowiedzi:

243

Masz rację. Jeśli chcesz usunąć użytkownika i wszystkie powiązane obiekty -> destroy_all Jeśli jednak chcesz usunąć użytkownika bez pomijania wszystkich powiązanych obiektów ->delete_all

Zgodnie z tym postem: Railsy: zależne =>: zniszcz VS: zależne =>: delete_all

  • destroy/ destroy_all: Powiązane obiekty są niszczone obok tego obiektu, wywołując ich metodę niszczenia
  • delete/ delete_all: Wszystkie powiązane obiekty są niszczone natychmiast, bez wywoływania metody: destroy
Sandro Munda
źródło
80
Należy również zauważyć, że 1) wywołania zwrotne nie są wywoływane podczas używania delete_all, oraz 2) destroy_alltworzy wszystkie rekordy i niszczy je pojedynczo, więc przy bardzo dużym zestawie danych może to być boleśnie powolne.
Dylan Markow
załóżmy, że korzystam z metody before_destroy w modelu - jeśli użyję delete_all, to ta metoda się nie uruchomi? po drugie, jeśli zastosuję metodę before_delete w moim modelu, czy to zadziała, kiedy uruchomię delete lub delete_all w konsoli rails?
BKSpurgeon
23

delete_all to pojedyncza instrukcja SQL DELETE i nic więcej. wywołania destroy_all destroy () na wszystkich pasujących wynikach: warunków (jeśli je masz), które mogą być co najmniej NUM_OF_RESULTS instrukcjami SQL.

Jeśli musisz zrobić coś drastycznego, np. Destroy_all () na dużym zestawie danych, prawdopodobnie nie zrobiłbym tego z aplikacji i nie obchodziłbym się z nim ręcznie. Jeśli zestaw danych jest wystarczająco mały, nie zaszkodziłbyś tak bardzo.

Ryan Her
źródło
16

Aby uniknąć faktu, że destroy_alltworzy on wszystkie rekordy i niszczy je pojedynczo, możesz użyć go bezpośrednio z klasy modelu.

Więc zamiast:

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

Możesz to zrobić :

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

Wynikiem jest jedno zapytanie, aby zniszczyć wszystkie powiązane rekordy

simon-olivier
źródło
1
Czy wywoła wywołania zwrotne zniszczenia w powiązanych rekordach, czy też jest UsageIndex.destroy_allrównoważne z UsageIntex.delete_all?
Magne
UsageIndex.destroy_allnie jest już dostępny, ponieważ szyny 3
fabriciofreitag
1

Zrobiłem mały klejnot, który może w niektórych okolicznościach zmniejszyć potrzebę ręcznego usuwania powiązanych rekordów.

Ten klejnot dodaje nową opcję dla powiązań ActiveRecord:

zależne:: delete_recursively

Po zniszczeniu rekordu wszystkie rekordy skojarzone przy użyciu tej opcji zostaną usunięte rekurencyjnie (tj. W różnych modelach), bez tworzenia żadnego z nich.

Zauważ, że podobnie jak zależne:: usuń lub zależne:: delete_all, ta nowa opcja nie wyzwala wywołań zwrotnych w zależności od / przed / po zniszczeniu rekordów zależnych.

Można jednak mieć zależne:: zniszczyć powiązania w dowolnym miejscu w łańcuchu modeli, które w innym przypadku są powiązane z zależnymi:: usuń_recursywnie. Opcja: destroy będzie działać normalnie w dowolnym miejscu w górę lub w dół linii, tworząc i niszcząc wszystkie odpowiednie rekordy, a tym samym uruchamiając ich wywołania zwrotne.

Janosch
źródło
To jest fantastyczne! Zastanawiam się, dlaczego nie więcej osób oglądało / grało w gwiazdki / rozwidlało go na githubie .. czy nadal działa dobrze?
Magne
@Magne Dzięki! Powinno działać. Testy działają na Ruby 2.4.1 i Rails 5.1.1. Do tej pory używałem go tylko prywatnie, a nie w głównych aplikacjach produkcyjnych, stąd główna wersja „0”, ale nigdy nie zauważyłem żadnych problemów. Jest to również dość proste, więc powinno być w porządku.
Janosch
Chłodny. :) Prowadzę projekt na Ruby 2.3.1 i „rails”, „~> 4.1.14” i niestety jestem zmuszony polegać na activerecord (~> 4.1.0) z powodu innych klejnotów. Widzę, że delete_recursively jest rozwiązany do 0.9.0. Czy istnieje starsza wersja, która działałaby z activerecord 4.1? Nie mogłem znaleźć żadnej w zakładce wydania na github.
Magne
1
@Magne Stwierdziłem, że tak naprawdę działa dla activerecord w wersji 4.1.14 i wydałem wersję Gem 1.0.0 ze swobodną zależnością. Pamiętaj jednak, że gałąź 4.1 Railsów nie otrzymuje już aktualizacji zabezpieczeń.
Janosch