Posso impostare l'eliminazione a cascata in Rails?


90

So che probabilmente è su Internet da qualche parte, ma non riesco a trovare la risposta qui su Stackoverflow, quindi ho pensato di aumentare un po 'la base di conoscenza qui.

Sono un principiante di Ruby and Rails, ma la mia azienda ci sta investendo abbastanza, quindi sto cercando di conoscerla un po 'più in dettaglio.

È stato difficile per me cambiare la mia mentalità per progettare un'applicazione dal "modello" piuttosto che dal database, quindi sto cercando di capire come farebbe tutto il lavoro di progettazione che ho svolto classicamente nel database nel Modello binari invece.

Quindi il compito più recente che mi sono dato è capire come configurare un modello di database Rails per eseguire eliminazioni a cascata? C'è un modo semplice per farlo? O dovrei entrare nel MySql e configurarlo?

Risposte:


106

puoi anche impostare l'opzione: dipendente su: delete_all. : delete_all emetterà una singola istruzione SQL per eliminare tutti i record figlio. a causa di ciò, l'utilizzo di: delete_all potrebbe fornire prestazioni migliori.

has_many :memberships, dependent: :delete_all

8
La tua spiegazione è confusa. Verrà utilizzata una singola istruzione SQL, ma il metodo destroy non verrà chiamato per ogni riga figlia. Devi usare destroy_all per questo.
John Topley

@ John - spero che le modifiche chiariscano la confusione. Grazie per la segnalazione.
Mike Breen

27
Assicurati di capire la differenza tra usare :delete_alle :destroyper questo. Entrambi causeranno la nrimozione dal database delle appartenenze figlio (1 livello per l'eliminazione [citazione necessaria] e per la distruzione (se i loro figli hanno distruzioni dipendenti)), ma :destroyistanzeranno ogni oggetto figlio ed :delete_alleseguiranno prima qualsiasi callback, mentre eseguiranno direttamente un Istruzione SQL DELETE nel database. :destroyè più lento per questo motivo, ma consente di avere callback quando un record viene distrutto. Circumventing Rails da un lato e potenziale istanziazione n ^ x dall'altro.
jstim

2
Suggerisco anche di impostare chiavi esterne del database. In questo modo i record vengono eliminati con un'unica operazione. Vedi la risposta qui sotto che ho pubblicato.
Hendrik

66

Sì, puoi, se stai usando una relazione come has_many, fai semplicemente questo

has_many :memberships, dependent: :destroy

Dan, quindi immagino che la mia prossima domanda sia se eseguo un comando db migrate lo imposterà effettivamente nel db? O la cascata è completamente gestita dai binari?
matt_dev

Sì, è gestito da binari. (Assicurati di aver sempre bisogno di eliminare tutte le righe correlate, però.)
Stein G. Strindhaug

@Matt: la riga has_many dovrebbe essere nella classe del modello, la migrazione non la aggiungerà per te.
Gareth

Preferisco questa soluzione perché funziona anche se il modello dipendente ha un'altra relazione
has_many

27

Contrariamente alla risposta fornita, suggerisco caldamente di farlo anche a livello di database. Nel caso in cui si abbiano processi diversi o un ambiente multithread, potrebbe accadere che i record non vengano eliminati correttamente. Inoltre, la chiave esterna del database rende le cose molto più veloci quando si eliminano molti dati.

Come nella risposta suggerita, fai questo:

has_many :memberships, dependent: :delete_all

Tuttavia, assicurati anche di impostare un file foreign_keyin una migrazione. In questo modo il database si occupa di eliminare automaticamente i record per te.

Per annullare i valori quando un'appartenenza viene eliminata, supponendo che tu abbia un modello utente:

add_foreign_key :users, :memberships, on_delete: :nullify

È inoltre possibile eliminare tutti i modelli ogni volta che viene eliminato un abbonamento

add_foreign_key :users, :memberships, on_delete: :cascade

Quindi posso usare sia "has_many: membership, dependance:: delete_all" che "add_foreign_key: users,: memberships, on_delete:: cascade"? Funzionerà bene?
Rubycon

2
Non avrai nemmeno bisogno di impostare il delete_allnel modello. La chiave esterna si occuperà di eliminare tutto correttamente per te a livello di database.
Hendrik

3
Sono curioso di sapere cosa succede quando fai entrambe le cose. Sembra che non dovrebbe avere un effetto negativo, ma qualcuno ha avuto una brutta esperienza con questa pratica di fare sia AR che DB?
James Klein

1
Il livello del database è quello che stavo cercando. Questa dovrebbe essere la risposta accettata secondo me. Gli altri sembrano funzionare solo se le mie query si attengono alle operazioni standard di ActiveRecord.
Brett Beatty

10

Tieni presente che delete_all non eseguirà alcun callback (come before_destroy e after_destroy) sui record figlio.


6

Sembra che questo plugin possa darti quello che stai cercando se vuoi che le eliminazioni a cascata si riflettano nella struttura del database reale:

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

Il formato per utilizzarlo in una migrazione sarebbe qualcosa del genere:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end

5
Quel collegamento è morto ma questa è un'alternativa più recente: github.com/matthuhiggins/foreigner
gdelfino
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.