Dapatkah saya mengatur penghapusan Bertingkat di Rails?

90

Saya tahu ini mungkin ada di Internet di suatu tempat tetapi saya tidak dapat menemukan jawabannya di sini di Stackoverflow jadi saya pikir saya dapat sedikit meningkatkan basis pengetahuan di sini.

Saya seorang pemula di Ruby and Rails tetapi perusahaan saya cukup berinvestasi di dalamnya jadi saya mencoba untuk mengetahuinya dengan sedikit lebih detail.

Sulit bagi saya untuk mengubah pola pikir saya untuk merancang aplikasi dari "model" bukan dari database, jadi saya mencoba mencari tahu bagaimana melakukan semua pekerjaan desain yang telah saya lakukan secara klasik di Database di Model rel sebagai gantinya.

Jadi tugas terbaru yang saya berikan kepada diri saya sendiri adalah mencari cara untuk mengonfigurasi model database Rails untuk melakukan penghapusan bertingkat? Adakah cara mudah untuk melakukan ini? Atau apakah saya harus masuk ke MySql dan mengaturnya?

matt_dev
sumber

Jawaban:

106

Anda juga dapat menyetel opsi: dependen ke: delete_all. : delete_all akan mengeluarkan pernyataan SQL tunggal untuk menghapus semua catatan anak. karena ini menggunakan: delete_all dapat memberi Anda kinerja yang lebih baik.

has_many :memberships, dependent: :delete_all
Mike Breen
sumber
8
Penjelasan Anda membingungkan. Pernyataan SQL tunggal akan digunakan, tetapi metode penghancuran tidak akan dipanggil untuk setiap baris anak. Anda harus menggunakan destroy_all untuk itu.
John Topley
@ John - semoga hasil edit menghilangkan kebingungan. terima kasih telah menunjukkannya.
Mike Breen
27
Pastikan Anda memahami perbedaan antara menggunakan :delete_alldan :destroyuntuk ini. Keduanya akan menyebabkan keanggotaan turunan (1 tingkat untuk dihapus [rujukan?] Dan nuntuk penghancuran (jika turunannya memiliki ketergantungan penghancuran)) untuk dihapus dari database, tetapi :destroyakan membuat instance setiap objek turunan dan menjalankan panggilan balik apa pun terlebih dahulu, sedangkan :delete_allakan langsung menjalankan Pernyataan SQL DELETE dalam database. :destroylebih lambat karena itu, tetapi memungkinkan Anda memiliki callback saat record dimusnahkan. Mengakali Rails di satu sisi dan potensi instansiasi n ^ x di sisi lain.
jstim
2
Saya sarankan juga menyiapkan database kunci asing. Dengan cara itu record dihapus dengan satu operasi. Lihat jawaban di bawah yang saya posting.
Hendrik
66

Ya Anda bisa, jika Anda menggunakan hubungan seperti has_many Anda hanya melakukan ini

has_many :memberships, dependent: :destroy
danmayer
sumber
Dan, Jadi saya kira pertanyaan saya berikutnya adalah jika saya menjalankan perintah migrasi db, apakah itu akan benar-benar mengaturnya di db? Atau apakah cascading sepenuhnya ditangani oleh rel?
matt_dev
Ya, itu ditangani oleh rel. (Namun, pastikan Anda benar-benar selalu perlu menghapus setiap baris terkait.)
Stein G. Strindhaug
@Matt - baris has_many harus ada di kelas model Anda, migrasi tidak akan menambahkannya untuk Anda.
Gareth
Saya lebih suka solusi ini karena ini juga berfungsi jika model dependen memiliki hubungan has_many lain
tpei
27

Bertentangan dengan jawaban yang diberikan, saya sangat menyarankan juga melakukan ini pada level database. Jika Anda memiliki proses yang berbeda atau lingkungan multi utas, hal itu bisa terjadi karena rekaman tidak dihapus dengan benar. Selain itu, kunci asing database membuat segalanya menjadi lebih cepat saat menghapus banyak data.

Seperti dalam jawaban yang disarankan, lakukan ini:

has_many :memberships, dependent: :delete_all

Namun juga pastikan untuk menyiapkan foreign_keydalam migrasi. Dengan cara itu database menangani penghapusan catatan secara otomatis untuk Anda.

Untuk membatalkan nilai ketika keanggotaan dihapus, dengan asumsi Anda memiliki model pengguna:

add_foreign_key :users, :memberships, on_delete: :nullify

Anda juga dapat menghapus semua model setiap kali keanggotaan dihapus

add_foreign_key :users, :memberships, on_delete: :cascade
Hendrik
sumber
Jadi dapatkah saya menggunakan "has_many: membershiphips, dependent:: delete_all" dan "add_foreign_key: users,: memberships, on_delete:: cascade"? Akankah ini bekerja dengan baik?
Rubycon
2
Anda bahkan tidak perlu menyiapkan delete_alldalam model. Kunci asing akan menangani penghapusan semuanya dengan benar untuk Anda di tingkat database.
Hendrik
3
Saya ingin tahu tentang apa yang terjadi jika Anda melakukan keduanya. Sepertinya itu seharusnya tidak memiliki efek negatif tetapi apakah ada yang memiliki pengalaman buruk dengan praktik melakukan level AR dan DB ini?
James Klein
1
Level database adalah apa yang saya cari. Ini harus menjadi jawaban yang diterima menurut saya. Yang lain sepertinya hanya berfungsi jika kueri saya tetap berpegang pada operasi ActiveRecord standar.
Brett Beatty
10

Ingatlah bahwa delete_all tidak akan menjalankan callback apa pun (seperti before_destroy dan after_destroy) pada rekaman anak.

Jarin Udom
sumber
6

Sepertinya plugin ini mungkin memberi Anda apa yang Anda cari jika Anda ingin penghapusan berjenjang tercermin dalam struktur database yang sebenarnya:

http://www.redhillonrails.org/foreign_key_migrations.html

Format untuk menggunakan ini dalam migrasi akan menjadi seperti ini:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end
Sean McMains
sumber
5
Tautan itu sudah mati tetapi ini adalah alternatif yang lebih baru: github.com/matthuhiggins/foreigner
gdelfino