web-dev-qa-db-fra.com

que fait on_delete sur les modèles Django?

Je connais assez bien Django, mais j'ai récemment remarqué qu'il existe une option on_delete=models.CASCADE avec les modèles. J'ai cherché la documentation correspondante mais je n'ai rien trouvé de plus:

Modifié dans Django 1.9:

on_delete peut maintenant être utilisé comme deuxième argument de position (auparavant, il était généralement passé sous forme d'argument de mot clé). Ce sera un argument nécessaire dans Django 2.0.

n 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? ( des astuces dans la documentation )

Quelles autres options sont disponibles ( si mon estimation est correcte )?

Où réside la documentation pour cela?

211
Marty

C'est le comportement à adopter lorsque l'objet référencé est supprimé. Ce n’est pas spécifique à Django, c’est un standard SQL.

Il existe 6 actions possibles à prendre lorsqu'un tel événement se produit:

  • CASCADE: lorsque l'objet référencé est supprimé, supprimez également les objets qui y sont référencés (par exemple, lorsque vous supprimez une publication de blog, vous pouvez également supprimer des commentaires). Équivalent SQL: CASCADE.
  • PROTECT: Interdire la suppression de l'objet référencé. Pour le supprimer, vous devez supprimer tous les objets qui le référencent manuellement. Équivalent SQL: RESTRICT.
  • SET_NULL: définissez la référence sur NULL (le champ doit pouvoir être 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 que ces commentaires ont été publiés par un utilisateur anonyme (ou supprimé). Équivalent SQL: SET NULL.
  • SET_DEFAULT: Définissez la valeur par défaut. Équivalent SQL: SET DEFAULT.
  • SET(...): Définir 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érençant un objet qui n'existe pas réellement). Équivalent SQL: NO ACTION.

Source: documentation Django

Voir aussi la documentation de PostGreSQL par exemple.

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

417
Antoine Pinsard

La méthode on_delete indique à Django quoi faire avec les instances de modèle qui dépendent de l'instance de modèle que vous supprimez. (par exemple, une relation ForeignKey). Le on_delete=models.CASCADE indique à Django de mettre en cascade l'effet de suppression, c'est-à-dire de continuer à supprimer également les modèles dépendants.

Voici un exemple plus concret. Supposons que vous avez un modèle Author qui est un ForeignKey dans un modèle Book. Désormais, si vous supprimez une instance du modèle Author, Django ne saura pas quoi faire avec les instances du modèle Book qui dépendent de cette instance du modèle Author. La méthode on_delete indique à Django quoi faire dans ce cas. Définir on_delete=models.CASCADE ordonnera à Django de mettre en cascade l'effet de suppression, c'est-à-dire de supprimer toutes les instances de modèle Book qui dépendent de l'instance de modèle Author que vous avez supprimée.

Remarque: on_delete deviendra un argument requis dans Django 2.0. Dans les anciennes versions, la valeur par défaut était CASCADE.

Voici la documentation officielle complète.

33
Himank Yadav

Pour info, le paramètre on_delete dans les modèles est inversé par rapport à ce que cela ressemble. Vous mettez "on_delete" sur une clé étrangère sur un modèle pour indiquer à Django ce qu'il faut faire si l'entrée FK que vous pointez sur votre enregistrement est supprimée. Les options les plus utilisées par notre boutique sont PROTECT, CASCADE et SET_NULL. Voici les règles de base que j'ai définies:

  1. Utilisez PROTECT lorsque votre FK pointe vers une table de consultation qui ne devrait vraiment pas changer et que certainement ne devrait pas provoquer la modification de votre table. Si quelqu'un tente de supprimer une entrée de cette table de correspondance, PROTECT l'empê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 correspondance. Cette dernière partie est critique. Si quelqu'un supprime le genre "Femme" de mon tableau de genre, je ne voudrais certainement pas que cela supprime instantanément toutes les personnes que j'ai dans mon tableau de personnes qui ont ce genre.
  2. Utilisez CASCADE lorsque votre FK pointe vers un enregistrement "parent". Donc, si une personne peut avoir plusieurs entrées de PersonEthnicity (il/elle peut être américain, noir et blanc) et que cette personne est supprimée, je vraiment voudrais veux "enfant" Entrées PersonEthnicity à supprimer. Ils ne sont pas pertinents sans la personne.
  3. Utilisez SET_NULL lorsque vous faites voulez que les utilisateurs soient autorisés à supprimer une entrée dans une table de correspondance, mais que vous souhaitiez tout de même conserver votre enregistrement. Par exemple, si une personne peut avoir une école secondaire, mais que si ce lycée disparaisse de ma table de correspondance ne m'importe pas vraiment, je dirais "on_delete = SET_NULL". Cela laisserait ma fiche personnelle là-bas; il faudrait simplement que le FK du lycée sur ma personne soit annulé. De toute évidence, vous devrez autoriser null = True sur ce FK.

Voici un exemple de 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'

En dernier point, saviez-vous que si non spécifiez on_delete (ou non), le comportement par défaut est CASCADE? Cela signifie que si quelqu'un supprimait une entrée de genre dans votre table de genre, tous les enregistrements de personne avec ce genre étaient également supprimés!

Je dirais: "En cas de doute, définissez on_delete = models.PROTECT." Ensuite, testez 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 est à noter que on_delete = CASCADE n'est en fait ajouté à aucune de vos migrations, si c'est le comportement que vous sélectionnez. J'imagine que c'est parce que c'est la valeur par défaut, donc mettre on_delete = CASCADE revient à ne rien mettre.

25
HelenM

Voici la réponse à votre question qui dit: pourquoi on utilise 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 clé étrangère nullable et que vous souhaitez la définir comme nulle 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: Cascade supprime; le défaut.

PROTECT: Empêchez la suppression de l'objet référencé en élevant ProtectedError, une sous-classe de Django.db.IntegrityError.

SET_NULL: Définit la ForeignKey null; cela n'est possible que si null est True.

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

3
Sonia Rani

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 une propriété qui 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 voulez "supprimer" ces enregistrements. Mais vous ne voulez pas vraiment qu'ils disparaissent. Pour de nombreuses raisons. Quelqu'un pourrait l'avoir supprimé accidentellement, ou pour l'audit et la 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 application est écrite.

Par exemple, certaines applications ont un champ "supprimé" qui vaut 0 ou 1. Et toutes leurs recherches et leurs 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 serveur, exclut tout ce qui est deleted == 1. Toutefois, si vous créez un rapport personnalisé ou une requête personnalisée pour extraire une liste d'enregistrements supprimés et, encore plus, pour voir quand il a été modifié (un autre champ) et par qui (c'est-à-dire par qui l'a supprimé et quand). c'est très avantageux du point de vue de la direction.

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

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

3
George Mogilevsky