Was macht on_delete bei Django-Modellen?

348

Ich bin mit Django ziemlich vertraut, habe aber kürzlich festgestellt, dass es eine on_delete=models.CASCADEOption für die Modelle gibt. Ich habe nach der gleichen Dokumentation gesucht, konnte aber nichts weiter finden als:

In Django 1.9 geändert:

on_deletekann jetzt als zweites Positionsargument verwendet werden (zuvor wurde es normalerweise nur als Schlüsselwortargument übergeben). Dies wird in Django 2.0 ein erforderliches Argument sein.

Ein Anwendungsbeispiel ist

from django.db import models

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

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

Was macht on_delete? ( Ich denke, die Aktionen müssen ausgeführt werden, wenn das Modell gelöscht wird )

Was macht models.CASCADEdas ( Hinweise in der Dokumentation )

Welche anderen Optionen stehen zur Verfügung ( wenn meine Vermutung richtig ist )?

Wo befindet sich die Dokumentation dazu?

Alles ist gut
quelle
Es gibt auch eine Antwort auf eine ähnliche Frage unter stackoverflow.com/questions/47914325/…
HelenM
1
Der Text dieser ähnlichen Frage ist jetzt unten in dieser Antwort aufgeführt. Es beginnt "FYI, der Parameter on_delete in Modellen ist rückwärts von dem, wie es sich anhört." Es bietet viel mehr Details als die ursprünglichen Antworten.
HelenM

Antworten:

777

Dies ist das Verhalten, das angewendet werden muss, wenn das referenzierte Objekt gelöscht wird. Es ist nicht spezifisch für Django, dies ist ein SQL-Standard.

Es gibt 6 mögliche Maßnahmen, wenn ein solches Ereignis eintritt:

  • CASCADE: Wenn das referenzierte Objekt gelöscht wird, löschen Sie auch die Objekte, auf die verwiesen wird (Wenn Sie beispielsweise einen Blog-Beitrag entfernen, möchten Sie möglicherweise auch Kommentare löschen). SQL-Äquivalent : CASCADE.
  • PROTECT: Verbieten Sie das Löschen des referenzierten Objekts. Um es zu löschen, müssen Sie alle Objekte löschen, die manuell darauf verweisen. SQL-Äquivalent : RESTRICT.
  • SET_NULL: Setzen Sie den Verweis auf NULL (erfordert, dass das Feld nullwertfähig ist). Wenn Sie beispielsweise einen Benutzer löschen, möchten Sie möglicherweise die Kommentare behalten, die er in Blog-Posts gepostet hat, aber sagen, dass sie von einem anonymen (oder gelöschten) Benutzer gepostet wurden. SQL-Äquivalent : SET NULL.
  • SET_DEFAULT: Legen Sie den Standardwert fest. SQL-Äquivalent : SET DEFAULT.
  • SET(...): Stellen Sie einen bestimmten Wert ein. Dieser ist nicht Teil des SQL-Standards und wird vollständig von Django verwaltet.
  • DO_NOTHING: Wahrscheinlich eine sehr schlechte Idee, da dies zu Integritätsproblemen in Ihrer Datenbank führen würde (Verweis auf ein Objekt, das tatsächlich nicht vorhanden ist). SQL-Äquivalent : NO ACTION.

Quelle: Django-Dokumentation

Siehe auch die Dokumentation von PostGreSQL zum Beispiel.

In den meisten Fällen CASCADEist das erwartete Verhalten, aber für jeden ForeignKey sollten Sie sich immer fragen, was das erwartete Verhalten in dieser Situation ist. PROTECTund SET_NULLsind oft nützlich. Wenn CASCADESie festlegen, wo dies nicht der Fall sein soll, können Sie möglicherweise Ihre gesamte Datenbank in Kaskade löschen, indem Sie einfach einen einzelnen Benutzer löschen.


Zusätzlicher Hinweis zur Verdeutlichung der Kaskadenrichtung

Es ist lustig zu bemerken, dass die Richtung der CASCADEAktion für viele Menschen nicht klar ist. Eigentlich ist es lustig zu bemerken, dass nur die CASCADEAktion nicht klar ist. Ich verstehe, dass das Kaskadenverhalten verwirrend sein kann, aber Sie müssen denken, dass es die gleiche Richtung wie jede andere Aktion ist . Wenn Sie also das Gefühl haben, dass Ihnen die CASCADERichtung nicht klar ist, bedeutet dies tatsächlich, dass Ihnen das on_deleteVerhalten nicht klar ist.

In Ihrer Datenbank wird ein Fremdschlüssel im Wesentlichen durch ein Ganzzahlfeld dargestellt, dessen Wert der Primärschlüssel des Fremdobjekts ist. Angenommen , Sie haben einen Eintrag comment_A , der einen Fremdschlüssel für einen Eintrag article_B enthält . Wenn Sie den Eintrag comment_A löschen , ist alles in Ordnung, article_B lebte früher ohne comment_A und kümmert sich nicht darum, ob er gelöscht wird. Wenn Sie jedoch article_B löschen , gerät comment_A in Panik! Es hat nie ohne article_B gelebt und braucht es, es ist Teil seiner Attribute ( article=article_Baber was ist * article_B ** ???). Hier wird on_deleteeingegriffen, um zu bestimmen, wie dieser Integritätsfehler behoben werden kannentweder mit den Worten:

  • "Nein! Bitte! Nicht! Ich kann nicht ohne dich leben!" (was PROTECTin SQL-Sprache gesagt wird)
  • "Okay, wenn ich nicht dein bin, dann bin ich niemand" (was gesagt wird SET_NULL)
  • "Auf Wiedersehen Welt, ich kann nicht ohne article_B leben" und Selbstmord begehen (das ist das CASCADEVerhalten).
  • "Es ist in Ordnung, ich habe einen Ersatzliebhaber, ich werde ab sofort auf article_C verweisen" ( SET_DEFAULToder sogar SET(...)).
  • "Ich kann mich der Realität nicht stellen, ich werde deinen Namen weiter nennen, auch wenn das das einzige ist, was mir noch bleibt!" ( DO_NOTHING)

Ich hoffe, es macht die Kaskadenrichtung klarer. :) :)

Antoine Pinsard
quelle
19
Eine dumme Frage, aber Kaskade sollte immer eine Richtung sein, oder? Dh wenn Comment ein Fremdschlüssel zum BlogPostLöschen von BlogPost vorhanden ist, sollte der Kommentar gelöscht werden, aber das Löschen des Kommentars sollte BlogPost nicht löschen, unabhängig von RDMS?
Anthony Manning-Franklin
20
@ AnthonyManningFranklin Sicher. Beim Löschen wird nur ausgelöst, wenn eine Referenz "defekt" ist. Dies ist nicht der Fall, wenn Sie einen Kommentar löschen, da Sie die Referenz gleichzeitig löschen.
Antoine Pinsard
6
Die Frage ist nicht dumm; Ich brauche diese Erklärung auch. Hier nehmen wir also an, dass die Beziehung einseitig ist, der Eigentümer der Beziehung ist Comment, wer das FK-Feld in seiner Tabelle hat, während er BlogPost"besitzt", Commentwenn wir über das reale Modell sprechen. Gut.
WesternGun
3
Es ist wichtig zu beachten, dass beim Festlegen eines on_delete in Django KEINE ON DELETE-Klausel in der Datenbank selbst erstellt wird. Das angegebene Verhalten (z. B. CASCADE) wirkt sich nur auf Löschvorgänge aus, die über Django ausgeführt werden, und nicht auf Löschvorgänge, die direkt in der Datenbank ausgeführt werden.
JoeMjr2
2
Tolle Erklärung. Bekommt eine positive Bewertung von mir!
Homunculus Reticulli
42

Die on_deleteMethode wird verwendet, um Django mitzuteilen, was mit Modellinstanzen zu tun ist, die von der zu löschenden Modellinstanz abhängen. (zB eine ForeignKeyBeziehung). Das on_delete=models.CASCADEweist Django an, den Löscheffekt zu kaskadieren, dh auch weiterhin die abhängigen Modelle zu löschen.

Hier ist ein konkreteres Beispiel. Angenommen, Sie haben ein AuthorModell, das sich ForeignKeyin einem BookModell befindet. Wenn Sie nun eine Instanz des AuthorModells löschen , weiß Django nicht, was mit Instanzen des BookModells zu tun ist , die von dieser Instanz des AuthorModells abhängen . Die on_deleteMethode teilt Django mit, was in diesem Fall zu tun ist. Durch die Einstellung on_delete=models.CASCADEwird Django angewiesen, den Löscheffekt zu kaskadieren, dh alle BookModellinstanzen zu löschen , die von der von AuthorIhnen gelöschten Modellinstanz abhängen .

Hinweis: on_deleteWird in Django 2.0 zu einem erforderlichen Argument. In älteren Versionen wird standardmäßig verwendet CASCADE.

Hier ist die gesamte offizielle Dokumentation.

him229
quelle
37

Zu Ihrer Information, der on_deleteParameter in Modellen ist rückwärts von dem, wie es sich anhört. Sie setzen on_deleteeinen Fremdschlüssel (FK) für ein Modell ein, um Django mitzuteilen, was zu tun ist, wenn der FK-Eintrag, auf den Sie in Ihrem Datensatz zeigen, gelöscht wird. Die Optionen unserem Shop verwendet haben , die meisten sind PROTECT, CASCADEund SET_NULL. Hier sind die Grundregeln, die ich herausgefunden habe:

  1. Verwenden PROTECTSie diese Option, wenn Ihr FK auf eine Nachschlagetabelle zeigt, die sich wirklich nicht ändern sollte und die sicherlich nicht dazu führen sollte, dass sich Ihre Tabelle ändert. Wenn jemand versucht, einen Eintrag in dieser Nachschlagetabelle zu löschen, wird PROTECTverhindert , dass er ihn löscht, wenn er an einen Datensatz gebunden ist. Es verhindert auch, dass Django Ihren Datensatz löscht , nur weil ein Eintrag in einer Nachschlagetabelle gelöscht wurde. Dieser letzte Teil ist kritisch. Wenn jemand das Geschlecht "weiblich" aus meiner Geschlechtstabelle löschen würde, würde ich BESTIMMT NICHT wollen, dass dadurch sofort alle Personen gelöscht werden, die ich in meiner Personentabelle hatte und die dieses Geschlecht hatten.
  2. Verwenden CASCADESie diese Option, wenn Ihre FK auf einen "übergeordneten" Datensatz zeigt. Also, wenn eine Person viele PersonEthnicity Einträge haben kann (kann er / sie indianischer, Schwarz und Weiß sein), und diese Person wird gelöscht, ich wirklich würde wollen alle „Kind“ PersonEthnicity Einträge gelöscht werden. Sie sind ohne die Person irrelevant.
  3. Verwenden SET_NULLSie diese Option, wenn Sie möchten, dass Personen einen Eintrag in einer Nachschlagetabelle löschen dürfen, Ihre Aufzeichnung jedoch beibehalten werden soll. Wenn zum Beispiel eine Person eine HighSchool haben kann, es mir aber nicht wirklich wichtig ist, ob diese High School auf meinem Nachschlagetisch verschwindet, würde ich sagen on_delete=SET_NULL. Dies würde meine Person Aufzeichnung dort draußen lassen; es würde nur die High-School-FK auf meiner Person auf null setzen. Natürlich müssen Sie null=Truediese FK zulassen .

Hier ist ein Beispiel eines Modells, das alle drei Dinge tut:

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'

Wussten Sie als letzten Leckerbissen, dass das Standardverhalten lautet , wenn Sie nichts angeben on_delete(oder nicht angeben ) CASCADE? Dies bedeutet, dass, wenn jemand einen Geschlechtseintrag in Ihrer Gender-Tabelle gelöscht hat, auch alle Personendatensätze mit diesem Geschlecht gelöscht wurden!

Ich würde sagen: "Wenn Sie Zweifel haben, setzen Sie on_delete=models.PROTECT." Dann testen Sie Ihre Anwendung. Sie werden schnell herausfinden, welche FKs mit den anderen Werten gekennzeichnet werden sollten, ohne Ihre Daten zu gefährden.

Es ist auch erwähnenswert, dass on_delete=CASCADEkeine Ihrer Migrationen tatsächlich hinzugefügt wird, wenn dies das von Ihnen ausgewählte Verhalten ist. Ich denke, das liegt daran, dass es die Standardeinstellung ist. Putten on_delete=CASCADEist also dasselbe wie Putten nichts.

HelenM
quelle
12

Wie bereits erwähnt, löscht CASCADE den Datensatz mit einem Fremdschlüssel und verweist auf ein anderes Objekt, das gelöscht wurde. Zum Beispiel, wenn Sie eine Immobilienwebsite und eine Immobilie haben, die auf eine Stadt verweist

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

und jetzt, wenn die Stadt aus der Datenbank gelöscht wird, werden auch alle zugehörigen Immobilien (z. B. Immobilien in dieser Stadt) aus der Datenbank gelöscht

Jetzt möchte ich auch die Vorzüge anderer Optionen erwähnen, wie SET_NULL oder SET_DEFAULT oder sogar DO_NOTHING. Grundsätzlich möchten Sie aus Verwaltungssicht diese Datensätze "löschen". Aber du willst nicht wirklich, dass sie verschwinden. Aus vielen Gründen. Jemand hat es möglicherweise versehentlich gelöscht oder zur Prüfung und Überwachung. Und einfache Berichterstattung. So kann es eine Möglichkeit sein, das Eigentum von einer Stadt zu "trennen". Auch hier kommt es darauf an, wie Ihre Bewerbung geschrieben ist.

Beispielsweise haben einige Anwendungen ein Feld "gelöscht", das 0 oder 1 ist. Alle Suchanfragen und Listenansichten usw., alles, was in Berichten oder überall dort angezeigt werden kann, wo der Benutzer vom Front-End aus darauf zugreifen kann, schließen alles aus, was ist deleted == 1. Wenn Sie jedoch einen benutzerdefinierten Bericht oder eine benutzerdefinierte Abfrage erstellen, um eine Liste der gelöschten Datensätze abzurufen, und noch mehr, um zu sehen, wann sie zuletzt geändert wurden (ein anderes Feld) und von wem (dh wer sie wann gelöscht hat). das ist aus exekutiver Sicht sehr vorteilhaft.

Und vergessen Sie nicht, dass Sie versehentliche Löschvorgänge so einfach wie deleted = 0bei diesen Datensätzen rückgängig machen können.

Mein Punkt ist, wenn es eine Funktionalität gibt, gibt es immer einen Grund dahinter. Nicht immer ein guter Grund. Aber ein Grund. Und oft auch eine gute.

George Mogilevsky
quelle
3
Dies war hilfreich, da klargestellt wurde, in welche Richtung die CASCADE auftritt. Die akzeptierte Antwort ist nicht klar, wenn Sie mit SQL-Kaskaden nicht vertraut sind.
Codescribblr
Vielen Dank :) sehr geschätzt!
George Mogilevsky
2
Ich stimme dieser Antwort zu, weil sie meinen Zweifel an der Richtung im Beziehungsmodell
beantwortet
6

Hier ist die Antwort auf Ihre Frage: Warum verwenden wir on_delete?

Wenn ein Objekt, auf das von einem ForeignKey verwiesen wird, gelöscht wird, emuliert Django standardmäßig das Verhalten der SQL-Einschränkung ON DELETE CASCADE und löscht auch das Objekt, das den ForeignKey enthält. Dieses Verhalten kann durch Angabe des Arguments on_delete überschrieben werden. Wenn Sie beispielsweise einen nullbaren ForeignKey haben und möchten, dass dieser beim Löschen des referenzierten Objekts auf null gesetzt wird:

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

Die möglichen Werte für on_delete finden Sie in django.db.models:

CASCADE: Kaskade wird gelöscht; der Standard.

PROTECT: Verhindern Sie das Löschen des referenzierten Objekts, indem Sie ProtectedError, eine Unterklasse von django.db.IntegrityError, auslösen.

SET_NULL: Setzen Sie den ForeignKey auf null. Dies ist nur möglich, wenn null True ist.

SET_DEFAULT: Setzen Sie den ForeignKey auf seinen Standardwert. Für den ForeignKey muss ein Standard festgelegt werden.

Sonia Rani
quelle
Einfache Worte machen es mir klar, da ich nicht mit SQL und Django reif bin. Vielen Dank.
wm.p1us
2

Angenommen, Sie haben zwei Modelle, eines mit dem Namen Person und eines mit dem Namen Companies .

Per Definition kann eine Person mehr als ein Unternehmen gründen.

Da ein Unternehmen nur eine Person haben kann, möchten wir, dass beim Löschen einer Person alle mit dieser Person verbundenen Unternehmen ebenfalls gelöscht werden.

Wir beginnen also damit, ein Personenmodell wie dieses zu erstellen

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

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

Dann kann das Unternehmensmodell so aussehen

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)

Beachten Sie die Verwendung on_delete=models.CASCADEin den Modellfirmen. Das heißt, alle Unternehmen zu löschen, wenn die Person, der es gehört (Instanz der Klasse Person), gelöscht wird.

Tiago Martins Peres 李大仁
quelle
1

Richten Sie Ihr mentales Modell der Funktionalität von "CASCADE" neu aus, indem Sie daran denken, einer bereits vorhandenen Kaskade (dh einem Wasserfall) eine FK hinzuzufügen. Die Quelle dieses Wasserfalls ist ein Primärschlüssel. Löscht den Fluss nach unten.

Wenn Sie also on_delete eines FK als "CASCADE" definieren, fügen Sie den Datensatz dieses FK zu einer Kaskade von Löschvorgängen hinzu, die von der PK stammen. Der FK-Datensatz kann an dieser Kaskade teilnehmen oder nicht ("SET_NULL"). In der Tat kann ein Datensatz mit einem FK sogar den Fluss der Löschvorgänge verhindern! Baue einen Damm mit "PROTECT".

Gregory
quelle
0

Die Verwendung von CASCADE bedeutet, Django tatsächlich anzuweisen, den referenzierten Datensatz zu löschen. Im folgenden Beispiel für eine Umfrage-App: Wenn eine 'Frage' gelöscht wird, werden auch die Auswahlmöglichkeiten dieser Frage gelöscht.

zB Frage: Wie haben Sie von uns erfahren? (Auswahlmöglichkeiten: 1. Freunde 2. Fernsehwerbung 3. Suchmaschine 4. E-Mail-Werbung)

Wenn Sie diese Frage löschen, werden auch alle diese vier Auswahlmöglichkeiten aus der Tabelle gelöscht. Beachten Sie, in welche Richtung es fließt. Sie müssen on_delete = models.CASCADE nicht in Frage stellen. Model hat es in die Auswahl aufgenommen.

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)
Kunal
quelle