Różnica między zniszczeniem a usunięciem

210

Jaka jest różnica pomiędzy

@model.destroy i @model.delete

Na przykład:

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

Czy to naprawdę ważne, czy korzystam z jednego, czy drugiego?

Saggex
źródło

Odpowiedzi:

289

Zasadniczo destroyuruchamia wszelkie wywołania zwrotne w modelu, podczas gdy deletenie.

Z interfejsu API Rails :

  • ActiveRecord::Persistence.delete

    Usuwa rekord w bazie danych i zawiesza to wystąpienie, aby pokazać, że nie należy wprowadzać żadnych zmian (ponieważ nie można ich utrwalić). Zwraca zamrożoną instancję.

    Wiersz jest po prostu usuwany za pomocą instrukcji SQL DELETE na kluczu podstawowym rekordu i żadne wywołania zwrotne nie są wykonywane.

    Aby wymusić wywołania zwrotne before_destroy i after_destroy obiektu lub dowolne: zależne opcje asocjacji, użyj #destroy.

  • ActiveRecord::Persistence.destroy

    Usuwa rekord w bazie danych i zawiesza to wystąpienie, aby pokazać, że nie należy wprowadzać żadnych zmian (ponieważ nie można ich utrwalić).

    Istnieje szereg wywołań zwrotnych związanych ze zniszczeniem. Jeśli wywołanie zwrotne before_destroy zwróci wartość false, akcja zostanie anulowana, a zniszczenie zwróci wartość false. Zobacz ActiveRecord :: Callbacki w celu uzyskania dalszych szczegółów.

Społeczność
źródło
Witaj @ user740584 - dziękuję za odpowiedź. Co rozumiesz przez „uruchamia jakieś wywołania zwrotne w modelu”?
BKSpurgeon
3
@BKSpurgeon ma na myśli ActiveRecord :: Callbacks: api.rubyonrails.org/classes/ActiveRecord/Callbacks.html . Jednym z takich połączeń zwrotnych jest model#before_destroymożliwość zatrzymania destroy()połączenia końcowego pod pewnymi warunkami.
Todd
102

delete usunie tylko bieżący rekord obiektu z db, ale nie skojarzy z nim powiązanych rekordów potomnych.

destroy usunie bieżący rekord obiektu z db, a także powiązany z nim rekord potomny z db.

Ich użycie naprawdę ma znaczenie:

Jeśli wiele obiektów nadrzędnych korzysta ze wspólnych obiektów podrzędnych, wówczas wywołanie destroyokreślonego obiektu nadrzędnego spowoduje usunięcie obiektów podrzędnych, które są współużytkowane przez innych wielu rodziców.

Taimoor Changaiz
źródło
5
Genialna odpowiedź. Dziękuję Ci. dodam, że terminologia, jak rozumiem, polega na tym, że dzieci są „zabijane”. brutalne dzieciobójstwo.
BKSpurgeon
W większości przypadków w produkcji chcesz użyć „zniszcz”
Outside_Box
Nie, to nie jest konieczne.
Taimoor Changaiz
Myślę, że słowem, którego powinieneś użyć, destroypotomkowie , a nie dzieci : zgodnie z dokumentacją zniszcz „tworzy nowy obiekt z atrybutów, a następnie wywołuje na nim zniszczenie”. rubydoc.info/docs/rails/4.1.7/ActiveRecord%2FRelation:destroy
Marco Lackovic
12

Podczas wywoływania destroylub destroy_allna ActiveRecordobiekcie ActiveRecordinicjowany jest proces „niszczenia”, analizuje usuwaną klasę, określa, co powinien zrobić dla zależności, uruchamia sprawdzanie poprawności itp.

Podczas wywoływania deletelub delete_allna obiekcie, ActiveRecordpo prostu próbuje uruchomić DELETE FROM tablename WHERE conditionszapytanie względem bazy danych, nie wykonując żadnych innych ActiveRecordzadań na tym samym poziomie.

nickcen
źródło
4

Tak, istnieje zasadnicza różnica między dwiema metodami Użyj delete_all, jeśli chcesz, aby rekordy były szybko usuwane bez wywoływania wywołań zwrotnych modelu

Jeśli zależy Ci na wywołaniach zwrotnych swoich modeli, użyj funkcji destroy_all

Z oficjalnych dokumentów

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

destroy_all (warunki = zero) public

Niszczy rekordy spełniające warunki, tworząc instancję każdego rekordu i wywołując jego metodę niszczenia. Wywołania zwrotne każdego obiektu są wykonywane (w tym: opcje powiązania zależnego oraz metody Observer before_destroy / after_destroy Observer). Zwraca kolekcję zniszczonych obiektów; każdy zostanie zamrożony, aby odzwierciedlić, że nie należy wprowadzać żadnych zmian (ponieważ nie można ich utrwalić).

Uwaga: Tworzenie instancji, wykonywanie oddzwonienia i usuwanie każdego rekordu może być czasochłonne, gdy usuwasz wiele rekordów jednocześnie. Generuje co najmniej jedno zapytanie SQL DELETE na rekord (lub więcej, aby wymusić wywołania zwrotne). Jeśli chcesz szybko usunąć wiele wierszy, bez obawy o ich powiązania lub wywołania zwrotne, użyj delete_all.

jamesc
źródło
2

Zasadniczo „usuń” wysyła zapytanie bezpośrednio do bazy danych w celu usunięcia rekordu. W takim przypadku Railsy nie wiedzą, jakie atrybuty znajdują się w rekordzie, który usuwa, ani nie ma żadnych wywołań zwrotnych (takich jak before_destroy).

Metoda „zniszczyć” pobiera przekazany identyfikator, pobiera model z bazy danych przy użyciu metody „znajdź”, a następnie wywołuje na nim funkcję zniszczenia. Oznacza to, że wywołania zwrotne są wyzwalane.

Możesz użyć opcji „usuń”, jeśli nie chcesz, aby wywołania zwrotne były wyzwalane lub chcesz poprawić wydajność. W przeciwnym razie (i przez większość czasu) będziesz chciał użyć opcji „zniszczyć”.

Severin
źródło
2

Wiele odpowiedzi już; chciałem wskoczyć na trochę więcej.

dokumenty :

W przypadku has_many, zniszcz i zniszcz_wszystko zawsze wywoła metodę zniszczenia usuwanego rekordu (rekordów), aby wywołać wywołania zwrotne. Jednak delete i delete_all albo wykonają usunięcie zgodnie ze strategią określoną przez opcję: zależną, lub jeśli nie zostanie podana opcja: zależna, będzie postępować zgodnie z domyślną strategią. Domyślną strategią jest nic nie robić (pozostawić klucze obce z ustawionymi identyfikatorami nadrzędnymi), z wyjątkiem has_many: through, gdzie domyślną strategią jest delete_all (usuń rekordy łączenia, bez uruchamiania wywołań zwrotnych).

deleteVerbage działa inaczej na ActiveRecord::Association.has_manyi ActiveRecord::Base. W tym drugim przypadku delete wykona SQL DELETEi obejdzie wszystkie sprawdzania poprawności / wywołania zwrotne. Ten pierwszy zostanie wykonany na podstawie :dependentopcji przekazanej do powiązania. Jednak podczas testowania znalazłem następujący efekt uboczny, w którym wywołania zwrotne były uruchamiane tylko dla, deletea nie dladelete_all

dependent: :destroy Przykład:

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]]
David Zhang
źródło