cosa fa on_delete sui modelli Django?


348

Conosco abbastanza bene Django, ma recentemente ho notato che esiste on_delete=models.CASCADEun'opzione con i modelli, ho cercato la documentazione per lo stesso ma non sono riuscito a trovare altro:

Modificato in Django 1.9:

on_deleteora può essere utilizzato come secondo argomento posizionale (in precedenza era in genere passato solo come argomento di parole chiave). Sarà un argomento obbligatorio in Django 2.0.

un esempio di utilizzo è

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

Cosa fa on_delete? ( Immagino che le azioni da fare se il modello viene eliminato )

Cosa fa models.CASCADE? ( eventuali suggerimenti nella documentazione )

Quali altre opzioni sono disponibili ( se la mia ipotesi è corretta )?

Dove risiede la documentazione per questo?


V'è anche una risposta a una domanda simile a stackoverflow.com/questions/47914325/...
HelenM

1
Il testo di questa domanda simile è ora elencato, di seguito, in questa risposta. Inizia "FYI, il parametro on_delete nei modelli è all'indietro da quello che sembra." Fornisce molti più dettagli rispetto alle risposte originali.
HelenM,

Risposte:


786

Questo è il comportamento da adottare quando viene eliminato l' oggetto referenziato . Non è specifico per Django, questo è uno standard SQL.

Ci sono 6 possibili azioni da intraprendere quando si verifica tale evento:

  • CASCADE: Quando l'oggetto a cui viene fatto riferimento viene eliminato, elimina anche gli oggetti a cui fa riferimento (ad esempio, quando rimuovi un post sul blog, potresti voler eliminare anche i commenti). SQL equivalente: CASCADE.
  • PROTECT: Vieta l'eliminazione dell'oggetto referenziato. Per eliminarlo dovrai eliminare tutti gli oggetti che lo fanno riferimento manualmente. SQL equivalente: RESTRICT.
  • SET_NULL: Imposta il riferimento su NULL (richiede che il campo sia nullable). Ad esempio, quando elimini un utente, potresti voler conservare i commenti che ha pubblicato sui post del blog, ma dire che è stato pubblicato da un utente anonimo (o eliminato). SQL equivalente: SET NULL.
  • SET_DEFAULT: Imposta il valore predefinito. SQL equivalente: SET DEFAULT.
  • SET(...): Imposta un dato valore. Questo non fa parte dello standard SQL ed è interamente gestito da Django.
  • DO_NOTHING: Probabilmente una pessima idea poiché ciò creerebbe problemi di integrità nel database (facendo riferimento a un oggetto che in realtà non esiste). SQL equivalente: NO ACTION.

Fonte: documentazione Django

Vedi anche la documentazione di PostGreSQL per esempio.

Nella maggior parte dei casi, CASCADEè il comportamento previsto, ma per ogni ForeignKey, dovresti sempre chiederti qual è il comportamento previsto in questa situazione. PROTECTe SET_NULLsono spesso utili. Impostando CASCADEdove non dovrebbe, è possibile eliminare potenzialmente tutto il database in cascata, semplicemente eliminando un singolo utente.


Nota aggiuntiva per chiarire la direzione della cascata

È divertente notare che la direzione CASCADEdell'azione non è chiara per molte persone. In realtà, è divertente notare che solo l' CASCADEazione non è chiara. Capisco che il comportamento in cascata potrebbe essere fonte di confusione, tuttavia è necessario pensare che sia la stessa direzione di qualsiasi altra azione . Pertanto, se ritieni che quella CASCADEdirezione non ti sia chiara, in realtà significa che il on_deletecomportamento non ti è chiaro.

Nel tuo database, una chiave esterna è sostanzialmente rappresentata da un campo intero il cui valore è la chiave primaria dell'oggetto esterno. Supponiamo che tu abbia una voce comment_A , che ha una chiave esterna per una voce article_B . Se elimini la voce comment_A , tutto va bene, article_B viveva senza comment_A e non preoccuparti se è stato eliminato. Tuttavia, se elimini article_B , comment_A panic! Non ha mai vissuto senza article_B e ne ha bisogno, fa parte dei suoi attributi ( article=article_B, ma cos'è * article_B ** ???). Qui è dove on_deleteinterviene, per determinare come risolvere questo errore di integrità, o dicendo:

  • "No! Per favore! No! Non posso vivere senza di te!" (che si dice PROTECTin linguaggio SQL)
  • "Va bene, se non sono tuo, allora non sono di nessuno" (che si dice SET_NULL)
  • "Arrivederci mondo, non posso vivere senza article_B" e suicidarmi (questo è il CASCADEcomportamento).
  • "Va bene, ho un amante di riserva, farò riferimento article_C da ora" ( SET_DEFAULTo anche SET(...)).
  • "Non posso affrontare la realtà, continuerò a chiamare il tuo nome anche se questa è l'unica cosa che mi rimane!" ( DO_NOTHING)

Spero che renda più chiara la direzione della cascata. :)


19
Una domanda sciocca, ma la cascata dovrebbe essere sempre una direzione giusta? Vale a dire se Comment ha una chiave esterna per BlogPosteliminare BlogPost dovrebbe eliminare Comment, ma eliminare Comment non dovrebbe eliminare BlogPost, indipendentemente da RDMS?
Anthony Manning-Franklin,

20
@AnthonyManningFranklin Certo. All'eliminazione viene attivato solo quando un riferimento è "rotto". Ciò non accade quando si elimina un commento, poiché si elimina il riferimento contemporaneamente.
Antoine Pinsard

6
La domanda non è sciocca; Ho bisogno anche di quella spiegazione. Quindi qui assumiamo che la relazione sia unilaterale, il proprietario della relazione è Comment, che ha il campo FK nella sua tabella, mentre BlogPost"possiede" Commentse parliamo del modello di vita reale. Buona.
WesternGun

3
Una cosa importante da notare è che l'impostazione di on_delete in Django NON crea una clausola ON DELETE nel database stesso. Il comportamento specificato (come CASCADE) influenzerà solo le eliminazioni eseguite tramite Django e non le eliminazioni non elaborate eseguite direttamente nel database.
JoeMjr2

2
Quelle citazioni alla fine sembrano tratte direttamente dai pannelli dei fumetti di Roy Lichtenstein! Incredibile
attacco aereo il

42

Il on_deletemetodo viene usato per dire a Django cosa fare con le istanze del modello che dipendono dall'istanza del modello che elimini. (ad es. una ForeignKeyrelazione). Questo on_delete=models.CASCADEdice a Django di mettere in cascata l'effetto di eliminazione, ovvero continuare a cancellare anche i modelli dipendenti.

Ecco un esempio più concreto. Supponiamo di avere un Authormodello che è ForeignKeyin un Bookmodello. Ora, se si elimina un'istanza del Authormodello, Django non saprebbe cosa fare con le istanze del Bookmodello che dipendono da quell'istanza del Authormodello. Il on_deletemetodo dice a Django cosa fare in quel caso. L'impostazione on_delete=models.CASCADEindicherà a Django di creare l'effetto a cascata dell'eliminazione, ovvero eliminare tutte le Bookistanze del modello che dipendono dall'istanza del modello che Authorhai eliminato.

Nota: on_deletediventerà un argomento obbligatorio in Django 2.0. Nelle versioni precedenti è l'impostazione predefinita CASCADE.

Ecco l'intera documentazione ufficiale.


37

Cordiali saluti, il on_deleteparametro nei modelli è all'indietro da quello che sembra. Metti on_deleteuna chiave esterna (FK) su un modello per dire a django cosa fare se la voce FK che stai indicando sul tuo record viene eliminata. Le opzioni nostro negozio hanno usato la maggior parte sono PROTECT, CASCADEe SET_NULL. Ecco le regole di base che ho capito:

  1. Utilizzare PROTECTquando l'FK punta a una tabella di ricerca che in realtà non dovrebbe cambiare e che certamente non dovrebbe far cambiare la tabella. Se qualcuno tenta di eliminare una voce in quella tabella di ricerca, PROTECTimpedisce di eliminarla se è associata a qualsiasi record. Impedisce inoltre a django di eliminare il record solo perché ha eliminato una voce in una tabella di ricerca. Quest'ultima parte è fondamentale. Se qualcuno dovesse eliminare il genere "Femmina" dalla mia tabella Sesso, Certamente NON vorrei che eliminasse istantaneamente tutte le persone che avevo nella mia tabella Persona che avevano quel genere.
  2. Utilizzare CASCADEquando l'FK punta a un record "genitore". Quindi, se una persona può avere molte voci PersonEthnicity (lui / lei può essere americano indiano, nero e bianco), e quella persona è cancellato, ho davvero avrebbe voglia qualsiasi "bambino" PersonEthnicity voci da eliminare. Sono irrilevanti senza la Persona.
  3. Utilizzare SET_NULLquando non desidera la gente a essere consentito di eliminare una voce su un look-up table, ma si vuole ancora conservare il record. Ad esempio, se una persona può avere una scuola superiore, ma per me non importa se quella scuola va via sul mio tavolo di ricerca, direi on_delete=SET_NULL. Questo lascerebbe il mio record personale là fuori; avrebbe semplicemente impostato l'FK del liceo sulla mia persona su null. Ovviamente, dovrai consentire null=Truesu quell'FK.

Ecco un esempio di un modello che fa tutte e tre le cose:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

Come ultima notizia, sapevi che se non specifichi on_delete(o non lo hai fatto), il comportamento predefinito è CASCADE? Ciò significa che se qualcuno ha eliminato una voce di genere nella tua tabella Sesso, anche tutti i record di Persona con quel genere sono stati eliminati!

Direi "In caso di dubbi, imposta on_delete=models.PROTECT ". Quindi prova l'applicazione. Capirai rapidamente quali FK dovrebbero essere etichettati con gli altri valori senza mettere in pericolo i tuoi dati.

Inoltre, vale la pena notare che in on_delete=CASCADErealtà non viene aggiunto a nessuna delle tue migrazioni, se questo è il comportamento che stai selezionando. Immagino che sia perché è l'impostazione predefinita, quindi mettere on_delete=CASCADEè la stessa cosa di non mettere nulla.


12

Come accennato in precedenza, CASCADE eliminerà il record che ha una chiave esterna e fa riferimento a un altro oggetto che è stato eliminato. Ad esempio, se hai un sito web immobiliare e una proprietà che fa riferimento a una città

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

e ora quando la città viene eliminata dal database, anche tutte le proprietà associate (ad es. immobili situati in quella città) verranno eliminate dal database

Ora voglio anche menzionare il merito di altre opzioni, come SET_NULL o SET_DEFAULT o persino DO_NOTHING. Fondamentalmente, dal punto di vista amministrativo, si desidera "eliminare" quei record. Ma non vuoi davvero che scompaiano. Per molte ragioni. Qualcuno potrebbe averlo eliminato per errore, o per il controllo e il monitoraggio. E rapporti semplici. Quindi può essere un modo per "disconnettere" la proprietà da una città. Ancora una volta, dipenderà da come è stata scritta l'applicazione.

Ad esempio, alcune applicazioni hanno un campo "eliminato" che è 0 o 1. E tutte le loro ricerche e visualizzazioni elenco ecc., Tutto ciò che può apparire nei report o ovunque l'utente possa accedervi dal front-end, esclude qualsiasi cosa sia deleted == 1. Tuttavia, se si crea un report personalizzato o una query personalizzata per visualizzare un elenco di record eliminati e ancora di più per vedere quando è stato modificato l'ultima volta (un altro campo) e da chi (ovvero chi lo ha eliminato e quando) .. questo è molto vantaggioso dal punto di vista esecutivo.

E non dimenticare che puoi ripristinare le cancellazioni accidentali semplici come deleted = 0quelle.

Il mio punto è che se c'è una funzionalità, c'è sempre una ragione dietro di essa. Non sempre una buona ragione. Ma una ragione. E spesso anche buono.


3
Ciò è stato utile perché ha chiarito in quale direzione si verifica CASCADE. La risposta accettata non è chiara se non si ha familiarità con le cascate SQL.
codescribblr

Grazie, molto apprezzato!
George Mogilevsky,

2
Ho votato a favore di questa risposta perché risponde al mio dubbio sulla direzione nel modello di relazione
edepe

6

Ecco la risposta alla tua domanda che dice: perché usiamo on_delete?

Quando un oggetto a cui fa riferimento un ForeignKey viene eliminato, Django per impostazione predefinita emula il comportamento del vincolo SQL ON DELETE CASCADE ed elimina anche l'oggetto contenente ForeignKey. Questo comportamento può essere ignorato specificando l'argomento on_delete. Ad esempio, se si dispone di un ForeignKey nullable e si desidera che sia impostato null quando l'oggetto a cui viene fatto riferimento viene eliminato:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

I valori possibili per on_delete si trovano in django.db.models:

CASCATA: eliminazioni in cascata; il predefinito.

PROTECT: Impedisci la cancellazione dell'oggetto referenziato sollevando ProtectedError, una sottoclasse di django.db.IntegrityError.

SET_NULL: imposta null su ForeignKey; questo è possibile solo se null è True.

SET_DEFAULT: imposta ForeignKey sul valore predefinito; è necessario impostare un valore predefinito per ForeignKey.


Parole semplici mi chiariscono perché non sono maturo con sql e django. Grazie.
wm.p1us

3

Supponiamo che tu abbia due modelli, uno di nome Person e un altro di nome Companies .

Per definizione, una persona può creare più di una società.

Considerando che una società può avere una e una sola persona, vogliamo che quando una persona viene eliminata vengano eliminate anche tutte le società associate a quella persona.

Quindi, iniziamo creando un modello Person, come questo

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

Quindi, il modello Aziende può apparire così

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

Si noti l'utilizzo on_delete=models.CASCADEnel modello Aziende. Ciò significa eliminare tutte le società quando viene eliminata la persona che la possiede (istanza della classe Person).


1

Riorienta il tuo modello mentale della funzionalità di "CASCADE" pensando di aggiungere un FK a una cascata già esistente (cioè una cascata). La fonte di questa cascata è una chiave primaria. Elimina il flusso verso il basso.

Quindi, se definisci on_delete di un FK come "CASCADE", stai aggiungendo il record di questo FK a una cascata di eliminazioni originate dal PK. Il record dell'FK può partecipare o meno a questa cascata ("SET_NULL"). In effetti, un record con un FK potrebbe persino impedire il flusso delle eliminazioni! Costruisci una diga con "PROTECT".


0

Usare CASCADE significa in realtà dire a Django di cancellare il record a cui si fa riferimento. Nell'esempio dell'app di sondaggio di seguito: Quando viene eliminata una "Domanda", verranno eliminate anche le Scelte presenti in questa Domanda.

es. Domanda: come ci hai conosciuto? (Scelte: 1. Amici 2. Annuncio TV 3. Motore di ricerca 4. Promozione e-mail)

Quando elimini questa domanda, eliminerà anche tutte e quattro le scelte dalla tabella. Notare quale direzione scorre. Non devi mettere on_delete = models.CASCADE in Question Model inseriscilo nella Scelta.

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)
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.