Il existe de nombreuses publications Stack Overflow sur la récursivité utilisant le signal post_save
, auxquelles les commentaires et les réponses sont en grande majorité: "pourquoi ne pas remplacer save ()" ou une sauvegarde qui est uniquement déclenchée sur created == True
.
Eh bien, j'estime qu'il est judicieux de ne pas utiliser save()
- par exemple, j'ajoute une application temporaire qui traite les données d'exécution de la commande de manière totalement distincte de notre modèle de commande.
Le reste de la structure ignore parfaitement l'application d'exécution et l'utilisation de hooks post_save isole tous les codes liés à l'exécution de notre modèle Order.
Si nous abandonnons le service d'exécution, rien de notre code principal ne doit changer. Nous supprimons l'application de réalisation, et c'est tout.
Donc, y a-t-il des méthodes décentes pour s'assurer que le signal post_save ne déclenche pas le même gestionnaire deux fois?
Que pensez-vous de cette solution?
@receiver(post_save, sender=Article)
def generate_thumbnails(sender, instance=None, created=False, **kwargs):
if not instance:
return
if hasattr(instance, '_dirty'):
return
do_something()
try:
instance._dirty = True
instance.save()
finally:
del instance._dirty
Vous pouvez également créer un décorateur
def prevent_recursion(func):
@wraps(func)
def no_recursion(sender, instance=None, **kwargs):
if not instance:
return
if hasattr(instance, '_dirty'):
return
func(sender, instance=instance, **kwargs)
try:
instance._dirty = True
instance.save()
finally:
del instance._dirty
return no_recursion
@receiver(post_save, sender=Article)
@prevent_recursion
def generate_thumbnails(sender, instance=None, created=False, **kwargs):
do_something()
vous pouvez utiliser update au lieu de sauvegarder dans le gestionnaire de signal
quersyset.filter (pk = instance.pk) .update (....)
Ne déconnectez pas les signaux. Si un nouveau modèle du même type est généré alors que le signal est déconnecté, la fonction de gestionnaire ne sera pas activée. Les signaux sont globaux sur Django et plusieurs demandes peuvent être exécutées simultanément, ce qui fait que certaines échouent tandis que d'autres exécutent leur gestionnaire post_save.
Je pense que créer une méthode save_without_signals()
sur le modèle est plus explicite:
class MyModel()
def __init__():
# Call super here.
self._disable_signals = False
def save_without_signals(self):
"""
This allows for updating the model from code running inside post_save()
signals without going into an infinite loop:
"""
self._disable_signals = True
self.save()
self._disable_signals = False
def my_model_post_save(sender, instance, *args, **kwargs):
if not instance._disable_signals:
# Execute the code here.
Pourquoi ne pas déconnecter puis reconnecter le signal dans votre fonction post_save
:
def my_post_save_handler(sender, instance, **kwargs):
post_save.disconnect(my_post_save_handler, sender=sender)
instance.do_stuff()
instance.save()
post_save.connect(my_post_save_handler, sender=sender)
post_save.connect(my_post_save_handler, sender=Order)
Vous devriez utiliser queryset.update () au lieu de Model.save () mais vous devez vous occuper d'autre chose:
Il est important de noter que lorsque vous l'utilisez, si vous souhaitez utiliser le nouvel objet, vous devez récupérer son objet à nouveau, car il ne modifiera pas l'objet self, par exemple:
>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> print el.text
>>> ''
Donc, si vous voulez utiliser le nouvel objet, vous devriez recommencer:
>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> el = MyModel.objects.get(pk=1) # Do it again
>>> print el.text
>>> 'Updated'
Vous pouvez également vérifier l'argument raw
dans post_save
, puis appeler save_base
instead de save
.
Regarde ça...
Chaque signal a ses propres avantages, comme vous pouvez le lire dans la documentation ici, mais je voulais partager quelques éléments à garder à l’esprit avec les signaux pre_save et post_save.
Les deux sont appelés à chaque fois que .save () est appelé sur un modèle. En d'autres termes, si vous enregistrez l'instance de modèle, les signaux sont envoyés.
l'exécution de save () sur l'instance dans un post_save peut souvent créer une boucle sans fin et donc provoquer une erreur de surchauffe de la récursivité maximale - uniquement si vous n'utilisez pas correctement .save ().
pre_save est idéal pour modifier uniquement les données d'instance car vous n'avez pas à appeler save (), ce qui élimine la possibilité susmentionnée. Vous n'avez pas à appeler save () car un signal de pré-enregistrement signifie littéralement juste avant d'être enregistré.
Les signaux peuvent appeler d'autres signaux et/ou exécuter des tâches différées (pour le céleri), ce qui peut être énorme pour la facilité d'utilisation.
Source: https://www.codingforentrepreneurs.com/blog/post-save-vs-pre-save-vs-override-save-method/
Cordialement!!