que fait on_delete sur les modèles Django?

348

Je suis assez familier avec Django, mais récemment j'ai remarqué qu'il existe une on_delete=models.CASCADEoption avec les modèles, j'ai cherché la documentation pour le même mais je n'ai rien trouvé de plus que:

Modifié dans Django 1.9:

on_deletepeut désormais être utilisé comme deuxième argument positionnel (auparavant, il n'était généralement passé qu'en tant qu'argument de mot clé). Ce sera un argument obligatoire dans Django 2.0.

un exemple d'utilisation est

from django.db import models

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

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

Que fait on_delete? ( Je suppose que les actions à faire si le modèle est supprimé )

Que fait models.CASCADE-il? ( toute indication dans la documentation )

Quelles autres options sont disponibles ( si ma supposition est correcte )?

Où se trouve la documentation pour cela?

Tout est possible
la source
Il y a aussi une réponse à une question similaire sur stackoverflow.com/questions/47914325/…
HelenM
1
Le texte de cette question similaire est maintenant répertorié, ci-dessous, sur cette réponse. Il commence "FYI, le paramètre on_delete dans les modèles est à l'envers de ce à quoi il ressemble." Il fournit beaucoup plus de détails que les réponses originales.
HelenM

Réponses:

780

Il s'agit du comportement à adopter lorsque l' objet référencé est supprimé. Ce n'est pas spécifique à django, c'est un standard SQL.

Il y a 6 actions possibles à entreprendre lorsqu'un tel événement se produit:

  • CASCADE: Lorsque l'objet référencé est supprimé, supprimez également les objets qui y font référence (lorsque vous supprimez un article de blog par exemple, vous pouvez également supprimer les commentaires). SQL équivalent: CASCADE.
  • PROTECT: Interdit la suppression de l'objet référencé. Pour le supprimer, vous devrez supprimer tous les objets qui le référencent manuellement. SQL équivalent: RESTRICT.
  • SET_NULL: Définissez la référence sur NULL (nécessite que le champ soit nullable). Par exemple, lorsque vous supprimez un utilisateur, vous souhaiterez peut-être conserver les commentaires qu'il a publiés sur les articles de blog, mais dire qu'il a été publié par un utilisateur anonyme (ou supprimé). SQL équivalent: SET NULL.
  • SET_DEFAULT: Définissez la valeur par défaut. SQL équivalent: SET DEFAULT.
  • SET(...): Définissez une valeur donnée. Celui-ci ne fait pas partie du standard SQL et est entièrement géré par Django.
  • DO_NOTHING: Probablement une très mauvaise idée car cela créerait des problèmes d'intégrité dans votre base de données (référencer un objet qui n'existe pas réellement). SQL équivalent: NO ACTION.

Source: documentation Django

Voir aussi la documentation de PostGreSQL par exemple.

Dans la plupart des cas, CASCADEc'est le comportement attendu, mais pour chaque ForeignKey, vous devez toujours vous demander quel est le comportement attendu dans cette situation. PROTECTet SET_NULLsont souvent utiles. Définir CASCADEoù il ne devrait pas, peut potentiellement supprimer toute votre base de données en cascade, en supprimant simplement un seul utilisateur.


Note supplémentaire pour clarifier la direction de la cascade

C'est drôle de remarquer que la direction de l' CASCADEaction n'est pas claire pour beaucoup de gens. En fait, c'est drôle de remarquer que seule l' CASCADEaction n'est pas claire. Je comprends que le comportement en cascade peut être déroutant, mais vous devez penser que c'est la même direction que toute autre action . Ainsi, si vous sentez que la CASCADEdirection n'est pas claire pour vous, cela signifie en fait que le on_deletecomportement n'est pas clair pour vous.

Dans votre base de données, une clé étrangère est essentiellement représentée par un champ entier dont la valeur est la clé primaire de l'objet étranger. Disons que vous avez une entrée comment_A , qui a une clé étrangère pour une entrée article_B . Si vous supprimez l'entrée comment_A , tout va bien, article_B utilisé pour vivre sans comment_A et ne dérange pas si elle est supprimée. Cependant, si vous supprimez article_B , alors comment_A panique! Il n'a jamais vécu sans article_B et en a besoin, cela fait partie de ses attributs ( article=article_B, mais qu'est-ce que * article_B ** ???). C'est là on_deletequ'intervient pour déterminer comment résoudre cette erreur d'intégrité, soit en disant:

  • "Non! S'il te plait! Non! Je ne peux pas vivre sans toi!" (ce qui est dit PROTECTen langage SQL)
  • "D'accord, si je ne suis pas à toi, alors je ne suis personne" (ce qui est dit SET_NULL)
  • "Au revoir monde, je ne peux pas vivre sans article_B" et me suicider (c'est le CASCADEcomportement).
  • "C'est bon, j'ai un amant de rechange, je vais faire référence à l'article_C à partir de maintenant" ( SET_DEFAULT, ou même SET(...)).
  • "Je ne peux pas faire face à la réalité, je continuerai à appeler ton nom même si c'est la seule chose qui me reste!" ( DO_NOTHING)

J'espère que cela rend la direction de la cascade plus claire. :)

Antoine Pinsard
la source
19
Une question idiote, mais la cascade devrait toujours être un droit directionnel? C'est-à-dire si Comment a une clé étrangère pour BlogPostsupprimer ensuite BlogPost devrait supprimer le commentaire, mais la suppression du commentaire ne devrait pas supprimer BlogPost, indépendamment du SGBD?
Anthony Manning-Franklin
20
@AnthonyManningFranklin Bien sûr. La suppression est déclenchée uniquement lorsqu'une référence est "cassée". Ce qui n'est pas le cas lorsque vous supprimez un commentaire, car vous supprimez la référence en même temps.
Antoine Pinsard
6
La question n'est pas idiote; J'ai aussi besoin de cette explication. Nous supposons donc ici que la relation est unilatérale, le propriétaire de la relation est celui Commentqui a le champ FK dans sa table, tandis que BlogPost"possède" Comments si nous parlons du modèle de la vie réelle. Bien.
WesternGun
3
Une chose importante à noter est que la définition d'un on_delete dans Django ne crée PAS de clause ON DELETE dans la base de données elle-même. Le comportement spécifié (tel que CASCADE) n'affectera que les suppressions effectuées via Django, et non les suppressions brutes effectuées directement dans la base de données.
JoeMjr2
2
Grande explication. Obtient un upvote de moi!
Homunculus Reticulli
42

La on_deleteméthode est utilisée pour indiquer à Django quoi faire avec les instances de modèle qui dépendent de l'instance de modèle que vous supprimez. (par exemple une ForeignKeyrelation). Le on_delete=models.CASCADEdit à Django de répercuter l'effet de suppression, c'est-à-dire de continuer à supprimer également les modèles dépendants.

Voici un exemple plus concret. Supposons que vous ayez un Authormodèle qui est ForeignKeydans un Bookmodèle. Maintenant, si vous supprimez une instance du Authormodèle, Django ne saurait pas quoi faire avec les instances du Bookmodèle qui dépendent de cette instance de Authormodèle. La on_deleteméthode indique à Django quoi faire dans ce cas. Le paramètre on_delete=models.CASCADEdemandera à Django de répercuter l'effet de suppression, c'est-à-dire de supprimer toutes les Bookinstances de modèle qui dépendent de l' Authorinstance de modèle que vous avez supprimée.

Remarque: on_deletedeviendra un argument obligatoire dans Django 2.0. Dans les anciennes versions, il est par défaut CASCADE.

Voici toute la documentation officielle.

him229
la source
37

Pour info, le on_deleteparamètre dans les modèles est à l'envers de ce à quoi il ressemble. Vous mettez on_deleteune clé étrangère (FK) sur un modèle pour dire à django quoi faire si l'entrée FK que vous pointez sur votre enregistrement est supprimée. Les options de notre boutique ont utilisé la plupart sont PROTECT, CASCADEet SET_NULL. Voici les règles de base que j'ai découvert:

  1. Utilisation PROTECT lorsque votre FK pointe vers une table de recherche qui ne devrait vraiment pas changer et qui ne devrait certainement pas faire changer votre table. Si quelqu'un essaie de supprimer une entrée de cette table de recherche, les PROTECTempêche de la supprimer si elle est liée à des enregistrements. Cela empêche également django de supprimer votre enregistrement simplement parce qu'il a supprimé une entrée sur une table de recherche. Cette dernière partie est critique. Si quelqu'un supprimait le sexe "Femme" de ma table Sexe, je ne voudrais certainement pas que cela supprime instantanément toutes les personnes que j'avais dans ma table Personne qui avaient ce sexe.
  2. À utiliser CASCADElorsque votre FK pointe vers un enregistrement "parent". Donc, si une personne peut avoir plusieurs entrées PersonEthnicity (il peut être un Indien de l'Amérique, un Noir et un Blanc) et que cette personne est supprimée, je voudrais vraiment que toutes les entrées PersonEthnicity "enfant" soient supprimées. Ils ne sont pas pertinents sans la personne.
  3. Utilisez SET_NULLlorsque vous ne voulez que les gens soient autorisés à supprimer une entrée sur une table de consultation, mais vous voulez toujours conserver votre dossier. Par exemple, si une personne peut avoir un lycée, mais cela n'a pas vraiment d'importance pour moi si ce lycée disparaît sur ma table de recherche, je dirais on_delete=SET_NULL. Cela laisserait mon dossier Personne là-bas; cela ne ferait que mettre le FK du secondaire sur ma personne à zéro. De toute évidence, vous devrez autoriser null=Truece FK.

Voici un exemple d'un modèle qui fait les trois choses:

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'

Comme dernière friandise, saviez-vous que si vous ne le faites pas spécifiezon_delete (ou pas), le comportement par défaut est CASCADE? Cela signifie que si quelqu'un a supprimé une entrée de sexe sur votre table de sexe, tous les enregistrements de personne avec ce sexe ont également été supprimés!

Je dirais: "En cas de doute, partez on_delete=models.PROTECT". Allez ensuite tester votre application. Vous découvrirez rapidement quels FK doivent être étiquetés comme les autres valeurs sans mettre en danger vos données.

En outre, il convient de noter que ce on_delete=CASCADEn'est en fait ajouté à aucune de vos migrations, si tel est le comportement que vous sélectionnez. Je suppose que c'est parce que c'est la valeur par défaut, donc mettre on_delete=CASCADEest la même chose que ne rien mettre.

HelenM
la source
12

Comme mentionné précédemment, CASCADE supprimera l'enregistrement qui a une clé étrangère et fait référence à un autre objet qui a été supprimé. Ainsi, par exemple, si vous avez un site Web immobilier et avez une propriété qui fait référence à une ville

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

et maintenant, lorsque la ville est supprimée de la base de données, toutes les propriétés associées (par exemple, les biens immobiliers situés dans cette ville) seront également supprimées de la base de données

Maintenant, je veux également mentionner le mérite d'autres options, telles que SET_NULL ou SET_DEFAULT ou même DO_NOTHING. Fondamentalement, du point de vue de l'administration, vous souhaitez "supprimer" ces enregistrements. Mais vous ne voulez pas vraiment qu'ils disparaissent. Pour de nombreuses raisons. Il est possible que quelqu'un l'ait supprimé accidentellement, ou à des fins d'audit et de surveillance. Et des rapports clairs. Cela peut donc être un moyen de «déconnecter» la propriété d'une ville. Encore une fois, cela dépendra de la façon dont votre demande est écrite.

Par exemple, certaines applications ont un champ "supprimé" qui est 0 ou 1. Et toutes leurs recherches et vues de liste, etc., tout ce qui peut apparaître dans les rapports ou n'importe où l'utilisateur peut y accéder depuis le frontal, exclut tout ce qui est deleted == 1. Cependant, si vous créez un rapport personnalisé ou une requête personnalisée pour dérouler une liste d'enregistrements qui ont été supprimés et plus encore pour voir quand il a été modifié pour la dernière fois (un autre champ) et par qui (c'est-à-dire qui l'a supprimé et quand). c'est très avantageux du point de vue exécutif.

Et n'oubliez pas que vous pouvez annuler les suppressions accidentelles aussi simple que deleted = 0pour ces enregistrements.

Mon point est, s'il y a une fonctionnalité, il y a toujours une raison derrière elle. Pas toujours une bonne raison. Mais une raison. Et souvent bon aussi.

George Mogilevsky
la source
3
Cela a été utile car il a précisé dans quelle direction la CASCADE se produit. La réponse acceptée n'est pas claire si vous n'êtes pas familier avec les cascades SQL.
codescribblr
Merci :) très apprécié!
George Mogilevsky
2
J'ai voté pour cette réponse car elle répond à mon doute sur la direction du modèle relationnel
edepe
6

Voici la réponse à votre question qui dit: pourquoi nous utilisons on_delete?

Lorsqu'un objet référencé par une ForeignKey est supprimé, Django émule par défaut le comportement de la contrainte SQL ON DELETE CASCADE et supprime également l'objet contenant la ForeignKey. Ce comportement peut être remplacé en spécifiant l'argument on_delete. Par exemple, si vous avez une ForeignKey nullable et que vous souhaitez qu'elle soit définie null lorsque l'objet référencé est supprimé:

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

Les valeurs possibles pour on_delete se trouvent dans django.db.models:

CASCADE: suppressions en cascade; le défaut.

PROTECT: empêche la suppression de l'objet référencé en augmentant ProtectedError, une sous-classe de django.db.IntegrityError.

SET_NULL: définissez la valeur ForeignKey null; cela n'est possible que si null est True.

SET_DEFAULT: définissez la clé étrangère sur sa valeur par défaut; une valeur par défaut pour ForeignKey doit être définie.

Sonia Rani
la source
Des mots simples m'indiquent clairement que je ne suis pas mature avec sql et django également. Je vous remercie.
wm.p1us
3

Disons que vous avez deux modèles, un nommé Personne et un autre nommé Entreprises .

Par définition, une personne peut créer plus d'une entreprise.

Étant donné qu'une entreprise peut avoir une et une seule personne, nous voulons que lorsqu'une personne est supprimée, toutes les entreprises associées à cette personne soient également supprimées.

Donc, nous commençons par créer un modèle Personne, comme celui-ci

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

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

Ensuite, le modèle Entreprises peut ressembler à ceci

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)

Remarquez l'utilisation de on_delete=models.CASCADEdans les sociétés modèles. Cela revient à supprimer toutes les sociétés lorsque la personne qui en est propriétaire (instance de la classe Personne) est supprimée.

Tiago Martins Peres 李大仁
la source
1

Réorientez votre modèle mental de la fonctionnalité de "CASCADE" en pensant ajouter un FK à une cascade déjà existante (ie une cascade). La source de cette cascade est une clé primaire. Supprime le flux vers le bas.

Donc, si vous définissez on_delete d'un FK comme "CASCADE", vous ajoutez l'enregistrement de ce FK à une cascade de suppressions provenant du PK. L'enregistrement du FK peut participer ou non à cette cascade ("SET_NULL"). En fait, un enregistrement avec un FK peut même empêcher le flux des suppressions! Construisez un barrage avec "PROTECT".

Gregory
la source
0

Utiliser CASCADE signifie en fait dire à Django de supprimer l'enregistrement référencé. Dans l'exemple de l'application de sondage ci-dessous: Lorsqu'une «question» est supprimée, elle supprime également les choix de cette question.

Ex. Question: Comment avez-vous entendu parler de nous? (Choix: 1. Amis 2. Publicité TV 3. Moteur de recherche 4. Promotion par e-mail)

Lorsque vous supprimez cette question, elle supprimera également ces quatre choix du tableau. Notez dans quelle direction il s'écoule. Vous n'avez pas à mettre on_delete = models.CASCADE dans Question Model le mettre dans le choix.

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
la source