web-dev-qa-db-fra.com

Identifier les champs modifiés dans le signal Django post_save

J'utilise le signal post_save de Django pour exécuter certaines instructions après avoir sauvegardé le modèle.

class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.BooleanField()


from Django.db.models.signals import post_save
from Django.dispatch import receiver

@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
        # do some stuff
        pass

Maintenant, je veux exécuter une instruction selon que la valeur du champ mode a changé ou non.

@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
        # if value of `mode` has changed:
        #  then do this
        # else:
        #  do that
        pass

J'ai consulté quelques fils SOF et un blog, mais je n'ai pas trouvé de solution à ce problème. Tous essayaient d'utiliser la méthode pre_save ou la forme qui ne sont pas mon cas d'utilisation. https://docs.djangoproject.com/es/1.9/ref/signals/#post-save dans la documentation Django ne mentionne pas de moyen direct de le faire.

Une réponse dans le lien ci-dessous semble prometteuse mais je ne sais pas comment l'utiliser. Je ne sais pas si la dernière version de Django le prend en charge ou non, car j’ai utilisé ipdb pour déboguer ceci et constaté que la variable instance ne comporte aucun attribut has_changed, comme indiqué dans la réponse ci-dessous.

Django: Lors de l'enregistrement, comment vérifier si un champ a été modifié?

26
Wendy

Configurez-le sur le __init__ de votre modèle pour y avoir accès. 

def __init__(self, *args, **kwargs):
    super(YourModel, self).__init__(*args, **kwargs)
    self.__original_mode = self.mode

Maintenant, vous pouvez effectuer quelque chose comme:

if instance.mode != instance.__original_mode:
    # do something useful
16
scoopseven

Habituellement, il est préférable de remplacer la méthode de sauvegarde plutôt que d’utiliser des signaux. 

De Deux scoops de Django : "Utilisez les signaux en dernier recours."

Je suis d'accord avec la réponse de @scoopseven sur la mise en cache de la valeur d'origine sur init, mais je remplace la méthode de sauvegarde si c'est possible.

class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.BooleanField()
    __original_mode = None

    def __init__(self, *args, **kwargs):
        super(Mode, self).__init__(*args, **kwargs)
        self.__original_mode = self.mode

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        if self.mode != self.__original_mode:
            #  then do this
        else:
            #  do that

        super(Mode, self).save(force_insert, force_update, *args, **kwargs)
        self.__original_mode = self.mode
24
educolo

si vous souhaitez comparer l'état avant et après l'action de sauvegarde, vous pouvez utiliser le signal pre_save qui fournit votre instance telle qu'elle devrait devenir après la mise à jour de la base de données. Vous pouvez également lire l'état actuel de l'instance dans la base de données et effectuer certaines actions en fonction de la différence. 

from Django.db.models.signals import post_save, pre_save
from Django.dispatch import receiver


@receiver(pre_save, sender=MyModel)
def on_cahnge(sender, instance: MyModel, **kwargs):
    if instance.id is None: # new object will be created
        pass # write your code hier
    else:
        previous = MyModel.objects.get(id=instance.id)
        if previous.field_a != instance.field_a: # fielad will be updated
            pass  # write your code hier
0
Ryabchenko Alexander