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.
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?
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.
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
.
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:
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.
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.
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.