delete_all vs destroy_all?

192

Saya mencari pendekatan terbaik untuk menghapus catatan dari sebuah tabel. Sebagai contoh, saya memiliki pengguna yang ID pengguna di banyak tabel. Saya ingin menghapus pengguna ini dan setiap catatan yang memiliki ID-nya di semua tabel.

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

Ini berfungsi dan menghapus semua referensi pengguna dari semua tabel, tapi saya dengar destroy_allitu sangat berat, jadi saya mencoba delete_all. Itu hanya menghapus pengguna dari tabel penggunanya sendiri dan iddari semua tabel lainnya dibuat nol, tetapi meninggalkan catatan di dalamnya. Dapatkah seseorang membagikan apa proses yang benar untuk melakukan tugas seperti ini?

Saya melihat bahwa destroy_allmemanggil destroyfungsi pada semua objek terkait tetapi saya hanya ingin mengkonfirmasi pendekatan yang benar.

glogis
sumber

Jawaban:

243

Kamu benar. Jika Anda ingin menghapus Pengguna dan semua objek terkait -> destroy_all Namun, jika Anda hanya ingin menghapus Pengguna tanpa menekan semua objek terkait ->delete_all

Menurut posting ini: Rails: dependen =>: destroy VS: depend =>: delete_all

  • destroy/ destroy_all: Objek terkait dihancurkan bersama objek ini dengan memanggil metode penghancuran mereka
  • delete/ delete_all: Semua objek yang terkait dihancurkan segera tanpa memanggil mereka: menghancurkan metode
Sandro Munda
sumber
80
Perlu juga dicatat bahwa 1) Callback tidak dipanggil saat menggunakan delete_all, dan 2) destroy_allinstantiate semua record dan hancurkan mereka satu per satu, jadi dengan dataset yang sangat besar, ini bisa sangat lambat.
Dylan Markow
misalkan saya menjalankan metode before_destroy dalam model - jika saya menggunakan delete_all maka metode ini tidak akan berjalan? kedua jika saya menggunakan metode before_delete dalam model saya, apakah ini akan berjalan ketika saya menjalankan delete atau delete_all di konsol rails?
BKSpurgeon
23

delete_all adalah pernyataan SQL DELETE tunggal dan tidak lebih. destroy_all calls destroy () pada semua hasil yang cocok dari: conditions (jika Anda memilikinya) yang setidaknya NUM_OF_RESULTS pernyataan SQL.

Jika Anda harus melakukan sesuatu yang drastis seperti destr_all () pada dataset besar, saya mungkin tidak akan melakukannya dari aplikasi dan menanganinya secara manual dengan hati-hati. Jika dataset cukup kecil, Anda tidak akan terluka banyak.

Ryan Her
sumber
16

Untuk menghindari fakta yang membuat destroy_allinstantiate semua record dan menghancurkannya satu per satu, Anda bisa menggunakannya langsung dari kelas model.

Jadi alih-alih:

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

Anda dapat melakukan :

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

Hasilnya adalah satu permintaan untuk memusnahkan semua catatan terkait

simon-olivier
sumber
1
Apakah akan memanggil penghancuran panggilan balik pada catatan terkait, atau UsageIndex.destroy_allsetara dengan UsageIntex.delete_all?
Magne
UsageIndex.destroy_alltidak lagi tersedia sejak rel 3
fabriciofreitag
1

Saya telah membuat permata kecil yang dapat mengurangi kebutuhan untuk menghapus catatan terkait secara manual dalam beberapa keadaan.

Permata ini menambahkan opsi baru untuk asosiasi ActiveRecord:

dependen:: delete_recursively

Saat Anda memusnahkan catatan, semua catatan yang dikaitkan dengan menggunakan opsi ini akan dihapus secara rekursif (yaitu lintas model), tanpa instantiasi salah satu dari mereka.

Perhatikan bahwa, sama seperti dependensi:: delete atau dependen:: delete_all, opsi baru ini tidak memicu panggilan balik sekitar / sebelum / sesudah_data dari catatan dependen.

Namun, ada kemungkinan untuk memiliki dependensi:: hancurkan asosiasi di mana saja dalam rantai model yang jika tidak dikaitkan dengan dependen:: delete_recursively. Opsi: destroy akan bekerja secara normal di mana saja di atas atau di bawah garis, membuat dan menghancurkan semua catatan yang relevan dan dengan demikian juga memicu panggilan balik mereka.

Janosch
sumber
Ini fantastis! Saya heran mengapa tidak banyak orang yang menonton / membintangi / bercabang di github .. apakah masih berfungsi dengan baik?
Magne
@Magne, terima kasih! Itu harus bekerja. Tes dijalankan pada Ruby 2.4.1 dan Rails 5.1.1. Sejauh ini saya hanya menggunakannya secara pribadi dan tidak di aplikasi produksi utama, maka versi utama "0", tapi saya tidak pernah melihat ada masalah. Ini juga cukup sederhana, jadi harus baik-baik saja.
Janosch
Keren. :) Saya menjalankan proyek di Ruby 2.3.1, dan 'rails', '~> 4.1.14', dan sayangnya terpaksa bergantung pada activerecord (~> 4.1.0) karena permata lain. Saya melihat delete_recursively diselesaikan ke 0.9.0. Apakah ada versi yang lebih lama yang dapat digunakan dengan activerecord 4.1? Saya tidak dapat menemukannya di tab rilis di github.
Magne
1
@Magne Saya menemukan itu benar-benar berfungsi untuk activerecord serendah 4.1.14 dan telah merilis versi permata 1.0.0 dengan ketergantungan yang santai. Ingatlah bahwa cabang 4.1 Rails tidak lagi menerima pembaruan keamanan.
Janosch