Différence entre détruire et supprimer

210

Quelle est la différence entre

@model.destroy et @model.delete

Par exemple:

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

Est-ce vraiment important si j'utilise l'un ou l'autre?

Saggex
la source

Réponses:

289

Exécute essentiellement destroytous les rappels sur le modèle alors deletequ'il ne le fait pas.

Depuis l' API Rails :

  • ActiveRecord::Persistence.delete

    Supprime l'enregistrement dans la base de données et fige cette instance pour indiquer qu'aucune modification ne doit être apportée (car elles ne peuvent pas être persistées). Renvoie l'instance gelée.

    La ligne est simplement supprimée avec une instruction SQL DELETE sur la clé primaire de l'enregistrement et aucun rappel n'est exécuté.

    Pour appliquer les rappels before_destroy et after_destroy de l'objet ou toute option d'association dépendante, utilisez #destroy.

  • ActiveRecord::Persistence.destroy

    Supprime l'enregistrement dans la base de données et fige cette instance pour indiquer qu'aucune modification ne doit être apportée (car elles ne peuvent pas être persistées).

    Il y a une série de rappels associés à destroy. Si le rappel before_destroy renvoie false, l'action est annulée et destroy renvoie false. Voir ActiveRecord :: Callbacks pour plus de détails.

Communauté
la source
Bonjour @ user740584 - merci pour votre réponse. Qu'entendez-vous par «exécute des rappels sur le modèle»?
BKSpurgeon
3
@BKSpurgeon, il signifie ActiveRecord :: Callbacks: api.rubyonrails.org/classes/ActiveRecord/Callbacks.html . Un tel rappel est celui model#before_destroyqui peut être utilisé pour interrompre l' destroy()appel final sous certaines conditions.
Todd
102

delete supprimera uniquement l'enregistrement d'objet actuel de la base de données, mais pas les enregistrements enfants associés de la base de données.

destroy supprimera l'enregistrement d'objet actuel de db et également son enregistrement enfant associé de db.

Leur utilisation compte vraiment:

Si vos objets parents multiples partagent des objets enfants communs, l'appel destroyà un objet parent spécifique supprimera les objets enfants qui sont partagés entre d'autres parents multiples.

Taimoor Changaiz
la source
5
Réponse brillante. Merci. j'ajouterais que la terminologie telle que je la comprends est que les enfants sont "tués". infanticide brutal.
BKSpurgeon
Dans la plupart des cas, en production, vous souhaitez utiliser «détruire»
Outside_Box
Non, ce n'est pas nécessaire.
Taimoor Changaiz,
Je pense que le mot que vous devriez utiliser destroyest descendant , pas enfant : selon la documentation, destroy "crée un nouvel objet à partir des attributs, puis appelle destroy dessus". rubydoc.info/docs/rails/4.1.7/ActiveRecord%2FRelation:destroy
Marco Lackovic
12

Lorsque vous invoquez destroyou destroy_allsur un ActiveRecordobjet, le ActiveRecordprocessus de `` destruction '' est lancé, il analyse la classe que vous supprimez, il détermine ce qu'il doit faire pour les dépendances, exécute les validations, etc.

Lorsque vous appelez deleteou delete_allsur un objet, ActiveRecordessaie simplement d'exécuter la DELETE FROM tablename WHERE conditionsrequête sur la base de données, sans effectuer d'autres ActiveRecordtâches de niveau.

nickcen
la source
4

Oui, il existe une différence majeure entre les deux méthodes Utilisez delete_all si vous souhaitez que les enregistrements soient supprimés rapidement sans que les rappels de modèle soient appelés

Si vous vous souciez des rappels de vos modèles, utilisez destroy_all

Des documents officiels

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

destroy_all (conditions = nil) public

Détruit les conditions de correspondance des enregistrements en instanciant chaque enregistrement et en appelant sa méthode destroy. Les rappels de chaque objet sont exécutés (y compris: les options d'association dépendantes et les méthodes Observer before_destroy / after_destroy). Renvoie la collection d'objets qui ont été détruits; chacun sera gelé, pour refléter qu'aucune modification ne devrait être apportée (puisqu'ils ne peuvent pas être persistés).

Remarque: L'instanciation, l'exécution de rappel et la suppression de chaque enregistrement peuvent prendre du temps lorsque vous supprimez plusieurs enregistrements à la fois. Il génère au moins une requête SQL DELETE par enregistrement (ou éventuellement plus, pour appliquer vos rappels). Si vous souhaitez supprimer rapidement de nombreuses lignes, sans vous soucier de leurs associations ou rappels, utilisez plutôt delete_all.

jamesc
la source
2

Fondamentalement, «supprimer» envoie une requête directement à la base de données pour supprimer l'enregistrement. Dans ce cas, Rails ne sait pas quels attributs sont dans l'enregistrement qu'il supprime ni s'il y a des rappels (tels que before_destroy).

La méthode "destroy" prend l'ID transmis, récupère le modèle de la base de données en utilisant la méthode "find", puis appelle destroy à ce sujet. Cela signifie que les rappels sont déclenchés.

Vous voudrez utiliser "supprimer" si vous ne voulez pas que les rappels soient déclenchés ou si vous voulez de meilleures performances. Sinon (et la plupart du temps) vous voudrez utiliser "destroy".

Severin
la source
2

Beaucoup de réponses déjà; voulait sauter avec un peu plus.

documents :

Pour has_many, destroy et destroy_all appellent toujours la méthode destroy des enregistrements supprimés afin que les rappels soient exécutés. Cependant, delete et delete_all effectueront la suppression conformément à la stratégie spécifiée par l'option: dependante, ou si aucune option: dependante n'est donnée, elle suivra la stratégie par défaut. La stratégie par défaut est de ne rien faire (laisser les clés étrangères avec les ID parents définis), sauf pour has_many: through, où la stratégie par défaut est delete_all (supprimer les enregistrements de jointure, sans exécuter leurs rappels).

Le deleteverbage fonctionne différemment pour ActiveRecord::Association.has_manyet ActiveRecord::Base. Pour ce dernier, delete exécutera SQL DELETEet contournera toutes les validations / rappels. Le premier sera exécuté en fonction de l' :dependentoption transmise à l'association. Cependant, lors des tests, j'ai trouvé l'effet secondaire suivant où les rappels n'étaient exécutés que pour deleteet nondelete_all

dependent: :destroy Exemple:

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
la source