2009-06-17 11 views
23

Un de mes modèles qui a ForeignKey est en fait une vue MySQL sur d'autres tables. Le problème que je suis en cours d'exécution en est que lorsque je supprime les données de ces tables, Django, comme décrit dans le "deleting objects" documentation ...Comment créer un modèle Django avec ForeignKeys qui ne cascade pas les suppressions à ses enfants?

When Django deletes an object, it emulates the behavior of the SQL constraint ON DELETE CASCADE -- in other words, any objects which had foreign keys pointing at the object to be deleted will be deleted along with it.

... essaie de supprimer des lignes de mon point de vue, ce qui bien sûr il peut « t, et ainsi jette l'erreur:

mysql_exceptions.OperationalError '>=(1395, "Can not delete from join view 'my_db.my_mysql_view'"' 

Est-il possible de spécifier une contrainte ForeignKey sur un modèle qui me fournira toutes les Django magie, mais ne cascade supprime sur elle? Ou est-il possible de demander à MySQL d'ignorer les commandes pour supprimer une ligne de ma vue au lieu de déclencher une erreur?

Répondre

18

réponse Harold m'a orienté dans la bonne direction. Ceci est une esquisse sur la façon dont j'implémenté (sur une base de données existante française, d'où la convention d'appellation un peu étrange):

class Factures(models.Model): 
    idFacture = models.IntegerField(primary_key=True) 
    idLettrage = models.ForeignKey('Lettrage', db_column='idLettrage', null=True, blank=True) 

class Paiements(models.Model): 
    idPaiement = models.IntegerField(primary_key=True) 
    idLettrage = models.ForeignKey('Lettrage', db_column='idLettrage', null=True, blank=True) 

class Lettrage(models.Model): 
    idLettrage = models.IntegerField(primary_key=True) 

    def delete(self): 
     """Dettaches factures and paiements from current lettre before deleting""" 
     self.factures_set.clear() 
     self.paiements_set.clear() 
     super(Lettrage, self).delete() 
+2

Voir 'on_delete = models.SET_NULL': https://docs.djangoproject.com/fr/1.4/ref/models/fields/#foreignkey (version sinze 1.3) – danihp

+2

Pourquoi' SET_NULL' aiderait? 'UPDATE's une vue pour définir une clé étrangère à NULL soulève toujours une exception. 'models.DO_NOTHING' aide cependant. – jnns

2

Eh bien, regardant méthode delete

def delete(self): 
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname) 

    # Find all the objects than need to be deleted. 
    seen_objs = CollectedObjects() 
    self._collect_sub_objects(seen_objs) 

    # Actually delete the objects. 
    delete_objects(seen_objs) 

Je dirais que remplaçant supprimer devrait être suffisant ... code non testé serait

def delete(self): 
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname) 

    # Find all the objects than need to be deleted. 
    seen_objs = CollectedObjects() 
    seen_objs.add(model=self.__class__, pk=self.pk, obj=self, parent_model=None) 

    # Actually delete the objects. 
    delete_objects(seen_objs) 
+0

(et oui, il est un peu en désordre, je dirais qu'il devrait y avoir argument ForeignKey ... remplir une demande de fonctionnalité pour elle dans le système de billets Django;)) Quoi qu'il en soit, pour le cas de votre vue , jetez un oeil à ce bug/patch: http://code.djangoproject.com/ticket/10829 – Almad

+0

en oubliant la suppression du jeu de queues. –

1

Une façon est d'appeler la méthode claire avant de supprimer, qui documentation here essentiellement « efface » la relation. Un problème pensé: ce n'est pas auto par lui-même. Vous pouvez choisir: appelez-le chaque fois que vous ne voulez pas de cascade, ou utilisez le signal pre_delete pour envoyer effacer avant chaque suppression, bien sûr cela vous donnera des problèmes lorsque vous voulez supprimer - cascade.

Ou vous pouvez contribuer à la communauté django et ajouter l'argument mot-clé à supprimer, ce sera peut-être dans django 1.3: D

3

Pour votre information - une demande de fonctionnalité pour cela existe dans le référentiel source django à http://code.djangoproject.com/ticket/7539. Il semble que ce sujet attire de l'attention. Espérons qu'il sera inclus dans les prochaines versions de Django. Le ticket inclut des correctifs au noyau de Django pour implémenter un paramètre facultatif "on_delete" sur models.ForeignKey (...) qui vous permet de spécifier ce qui se passe lorsque le pointé vers Model est supprimé, y compris désactiver la valeur par défaut ON DELETE CASCADE comportement.

+1

Yep - c'est touché 1.3 -> docs.djangoproject.com/fr/dev/releases/1.3-alpha-1/#configurable-delete-cascade –

+0

Merci pour la mise à jour @stevejalim. Bonnes nouvelles. Je voulais cette fonctionnalité depuis longtemps. – zlovelady

23

Django 1.3a1 et plus supportent cela via l'argument on_delete de ForeignKey.

L'exemple suivant définit le champ NULL lors de la suppression de la clé étrangère.Voir le documentation pour plus d'options.

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