delete_all vs destroy_all?

196

Je recherche la meilleure approche pour supprimer des enregistrements d'une table. Par exemple, j'ai un utilisateur dont l'ID utilisateur figure sur de nombreuses tables. Je veux supprimer cet utilisateur et chaque enregistrement qui a son ID dans toutes les tables.

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

Cela fonctionne et supprime toutes les références de l'utilisateur de toutes les tables, mais j'ai entendu dire que le destroy_allprocessus était très lourd, alors j'ai essayé delete_all. Il supprime uniquement l'utilisateur de sa propre table utilisateur et le idde toutes les autres tables est rendu nul, mais laisse les enregistrements intacts. Quelqu'un peut-il partager le processus correct pour effectuer une tâche comme celle-ci?

Je vois que cela destroy_allappelle la destroyfonction sur tous les objets associés mais je veux juste confirmer la bonne approche.

glogique
la source

Réponses:

249

Tu as raison. Si vous souhaitez supprimer l'utilisateur et tous les objets associés -> destroy_all Cependant, si vous souhaitez simplement supprimer l'utilisateur sans supprimer tous les objets associés ->delete_all

D'après ce post: Rails: dépendants =>: détruis VS: dépendants =>: delete_all

  • destroy/ destroy_all: Les objets associés sont détruits à côté de cet objet en appelant leur méthode destroy
  • delete/ delete_all: Tous les objets associés sont détruits immédiatement sans appeler leur méthode: destroy
Sandro Munda
la source
82
Il convient également de noter que 1) les rappels ne sont pas appelés lors de l'utilisation delete_all, et 2) destroy_allinstancie tous les enregistrements et les détruit un à la fois, donc avec un très grand ensemble de données, cela pourrait être très lent.
Dylan Markow le
supposons que j'exécute une méthode before_destroy dans le modèle - si j'utilise delete_all, cette méthode ne fonctionnera pas? deuxièmement, si j'emploie une méthode before_delete dans mon modèle, cela fonctionnera-t-il lorsque j'exécute delete ou delete_all dans la console des rails?
BKSpurgeon
24

delete_all est une seule instruction SQL DELETE et rien de plus. destroy_all appelle destroy () sur tous les résultats correspondants de: conditions (si vous en avez une) qui pourraient être au moins NUM_OF_RESULTS instructions SQL.

Si vous devez faire quelque chose de radical comme destroy_all () sur un grand ensemble de données, je ne le ferais probablement pas à partir de l'application et je le gérerais manuellement avec soin. Si l'ensemble de données est suffisamment petit, vous ne feriez pas autant de mal.

Ryan elle
la source
16

Pour éviter d' destroy_allinstancier tous les enregistrements et de les détruire un par un, vous pouvez l'utiliser directement à partir de la classe de modèle.

Donc au lieu de:

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

Tu peux faire :

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

Le résultat est une requête pour détruire tous les enregistrements associés

simon-olivier
la source
1
Appellera-t-il les rappels de destruction sur les enregistrements associés, ou est-il UsageIndex.destroy_alléquivalent à UsageIntex.delete_all?
Magne
UsageIndex.destroy_alln'est plus disponible depuis rails 3
fabriciofreitag
1

J'ai créé un petit bijou qui peut alléger la nécessité de supprimer manuellement les enregistrements associés dans certaines circonstances.

Ce joyau ajoute une nouvelle option pour les associations ActiveRecord:

dépendant:: delete_recursively

Lorsque vous détruisez un enregistrement, tous les enregistrements associés à l'aide de cette option seront supprimés de manière récursive (c'est-à-dire entre les modèles), sans instancier aucun d'entre eux.

Notez que, tout comme depend:: delete ou dependant:: delete_all, cette nouvelle option ne déclenche pas les callbacks around / before / after_destroy des enregistrements dépendants.

Cependant, il est possible d'avoir des associations dépendantes:: destroy n'importe où dans une chaîne de modèles qui sont autrement associées à dependants:: delete_recursively. L'option: destroy fonctionnera normalement n'importe où en haut ou en bas de la ligne, instanciant et détruisant tous les enregistrements pertinents et déclenchant ainsi leurs rappels.

Janosch
la source
C'est fantastique! Je me demande pourquoi pas plus de gens l'ont regardé / étoilé / bifurqué sur github .. est-ce que ça marche toujours bien?
Magne
@Magne Merci! Cela devrait fonctionner. Les tests s'exécutent sur Ruby 2.4.1 et Rails 5.1.1. Jusqu'à présent, je ne l'ai utilisé qu'en privé et non dans les principales applications de production, d'où la version majeure "0", mais je n'ai jamais remarqué de problèmes. C'est aussi assez simple, donc ça devrait aller.
Janosch
Cool. :) Je lance un projet sur Ruby 2.3.1, et 'rails', '~> 4.1.14', et je suis malheureusement obligé de compter sur activerecord (~> 4.1.0) à cause d'autres gemmes. Je vois que delete_recursively est résolu en 0.9.0. Existe-t-il une ancienne version qui fonctionnerait avec activerecord 4.1? Je n'en ai pas trouvé dans l'onglet des versions sur github.
Magne
1
@Magne J'ai trouvé que cela fonctionne réellement pour activerecord aussi bas que 4.1.14 et j'ai publié la version 1.0.0 de gem avec une dépendance relâchée. Gardez à l'esprit que la branche 4.1 de Rails ne reçoit plus les mises à jour de sécurité.
Janosch