24👍
Perhaps I’m wrong, but I think that category.save()
is not needed in your code, add() is enough because change is made in descendant but in category.
Also, to avoid signals you can:
- Disconnect signal and reconnect.
- Use update:
Descendant.objects.filter( pk = descendant.pk ).update( category = category )
21👍
To disable a signal on your model, a simple way to go is to set an attribute on the current instance to prevent upcoming signals firing.
This can be done using a simple decorator that checks if the given instance has the ‘skip_signal’ attribute, and if so prevents the method from being called:
from functools import wraps
def skip_signal():
def _skip_signal(signal_func):
@wraps(signal_func)
def _decorator(sender, instance, **kwargs):
if hasattr(instance, 'skip_signal'):
return None
return signal_func(sender, instance, **kwargs)
return _decorator
return _skip_signal
You can now use it this way:
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=MyModel)
@skip_signal()
def my_model_post_save(sender, instance, **kwargs):
instance.some_field = my_value
# Here we flag the instance with 'skip_signal'
# and my_model_post_save won't be called again
# thanks to our decorator, avoiding any signal recursion
instance.skip_signal = True
instance.save()
Hope This helps.
- [Django]-Django vs other Python web frameworks?
- [Django]-How does pgBouncer help to speed up Django
- [Django]-How to implement a "back" link on Django Templates?
2👍
Here is solution to temporary disable signal receiver
per instance which allows to use it on production (bc it is thread-safe)
[usage.py]
from django.db.models.signals import post_save
payment = Payment()
with mute_signals_for(payment, signals=[post_save]):
payment.save() # handle_payment signal receiver will be skipped
[code.py]
from contextlib import contextmanager
from functools import wraps
MUTE_SIGNALS_ATTR = '_mute_signals'
def mutable_signal_receiver(func):
"""Decorator for signals to allow to skip them by setting attr MUTE_SIGNALS_ATTR on instance,
which can be done via mute_signals_for"""
@wraps(func)
def wrapper(sender, instance, signal, **kwargs):
mute_signals = getattr(instance, MUTE_SIGNALS_ATTR, False)
if mute_signals is True:
pass # skip all signals
elif isinstance(mute_signals, list) and signal in mute_signals:
pass # skip user requested signal
else: # allow signal receiver
return func(sender=sender, instance=instance, signal=signal, **kwargs)
return wrapper
@contextmanager
def mute_signals_for(instance, signals):
"""Context manager to skip signals for @instance (django model), @signals can be
True to skip all signals or list of specified signals, like [post_delete, post_save] """
try:
yield setattr(instance, MUTE_SIGNALS_ATTR, signals)
finally:
setattr(instance, MUTE_SIGNALS_ATTR, False)
[signals.py]
@receiver(post_save, sender=Payment, dispatch_uid='post_payment_signal')
@mutable_signal_receiver
def handle_payment(sender, instance, created, **kwargs):
"""called after payment is registered in the system."""
- [Django]-Django: accessing session variables from within a template?
- [Django]-DRF: custom ordering on related serializers
- [Django]-Do I need to add a db_index to this Django model?
1👍
Where the previous answers demonstrate how to do this without external libraries, django-model-utils offers a clean way to do exactly this. The advantage of the library is that the code is explicit. Performing a bulk_create or filter followed by an update doesn’t make it clear that you want to disable the signal. Another advantage is that the save() method may perform additional validation.
from model_utils.models import SaveSignalHandlingModel
class Film(SaveSignalHandlingModel):
title = models.CharField(max_length=100)
film = Film(title="Cidade de Deus")
film.save(signals_to_disable=["post_save"])
See the post here: https://django.wtf/blog/disable-django-model-signals/
- [Django]-Django type object Http404 has no attribute get
- [Django]-Django Rest Framework – Missing Static Directory
- [Django]-Show page items count in django pagination