45👍
What you think about this 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
You can also create decorator
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()
96👍
you can use update instead of save in the signal handler
queryset.filter(pk=instance.pk).update(....)
- [Django]-Create custom buttons in admin change_form in Django
- [Django]-Module "django.core.context_processors" does not define a "auth" callable request processor
- [Django]-Django: How can I protect against concurrent modification of database entries
38👍
Don’t disconnect signals. If any new model of the same type is generated while the signal is disconnected the handler function won’t be fired. Signals are global across Django and several requests can be running concurrently, making some fail while others run their post_save handler.
- [Django]-Unit Testing a Django Form with a FileField
- [Django]-How do I output HTML in a message in the new Django messages framework?
- [Django]-How to get the domain name of my site within a Django template?
29👍
I think creating a save_without_signals()
method on the model is more explicit:
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.
- [Django]-Download a remote image and save it to a Django model
- [Django]-Django Aggregation – Expression contains mixed types. You must set output_field
- [Django]-Django TemplateDoesNotExist?
19👍
How about disconnecting then reconnecting the signal within your post_save
function:
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)
- [Django]-How to change empty_label for modelForm choice field?
- [Django]-How to validate an Email address in Django?
- [Django]-Different db for testing in Django?
4👍
You should use queryset.update() instead of Model.save() but you need to take care of something else:
It’s important to note that when you use it, if you want to use the new object you should get his object again, because it will not change the self object, for example:
>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> print el.text
>>> ''
So, if you want to use the new object you should do again:
>>> 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'
- [Django]-Django: Converting an entire set of a Model's objects into a single dictionary
- [Django]-Django: best practice for splitting up project into apps
- [Django]-Django QuerySet order
4👍
You could also check the raw
argument in post_save
and then call save_base
instead of save
.
- [Django]-The view didn't return an HttpResponse object. It returned None instead
- [Django]-Allowing only super user login
- [Django]-Django applying a style class based on a conditional
1👍
the Model’s .objects.update()
method bypasses the post_save
signal
Try this something like this:
from django.db import models
from django.db.models.signals import post_save
class MyModel(models.Model):
name = models.CharField(max_length=200)
num_saves = models.PositiveSmallIntegerField(default=0)
@classmethod
def post_save(cls, sender, instance, created, *args, **kwargs):
MyModel.objects.filter(id=instance.id).update(save_counter=instance.save_counter + 1)
post_save.connect(MyModel.post_save, sender=MyModel)
In this example, an object has a name and each time .save()
is called, the .num_saves
property is incremented, but without recursion.
- [Django]-What's the differences between has_object_permission and has_permission?
- [Django]-Tool for pinpointing circular imports in Python/Django?
- [Django]-CharField with fixed length, how?
0👍
Check this out…
Each signal has it’s own benefits as you can read about in the docs here but I wanted to share a couple things to keep in mind with the pre_save and post_save signals.
-
Both are called every time .save() on a model is called. In other words, if you save the model instance, the signals are sent.
-
running save() on the instance within a post_save can often create a never ending loop and therefore cause a max recursion depth exceeded error — only if you don’t use .save() correctly.
-
pre_save is great for changing just instance data because you do not have to call save() ever which eliminates the possibility for above. The reason you don’t have to call save() is because a pre_save signal literally means right before being saved.
-
Signals can call other signals and or run delayed tasks (for Celery) which can be huge for usability.
Source: https://www.codingforentrepreneurs.com/blog/post-save-vs-pre-save-vs-override-save-method/
Regards!!
- [Django]-Unique BooleanField value in Django?
- [Django]-How to deal with "SubfieldBase has been deprecated. Use Field.from_db_value instead."
- [Django]-Django character set with MySQL weirdness
0👍
In post_save singal in django for avoiding recursion ‘if created’ check is required
from django.dispatch import receiver
from django.db.models.signals import post_save
@receiver(post_save, sender=DemoModel)
def _post_save_receiver(sender,instance,created, **kwargs):
if created:
print('hi..')
instance.save()
- [Django]-How to use custom manager with related objects?
- [Django]-Is there a built-in login template in Django?
- [Django]-Malformed Packet: Django admin nested form can't submit, connection was reset
0👍
I was using the save_without_signals()
method by @Rune Kaagaard until i updated my Django to 4.1. On Django 4.1 this method started raising an Integrity error on the database that gave me 4 days of headaches and i couldn’t fix it.
So i started to use the queryset.update()
method and it worked like a charm. It doesn’t trigger the pre_save()
neither post_save()
and you don’t need to override the save()
method of your model. 1 line of code.
@receiver(pre_save, sender=Your_model)
def any_name(sender, instance, **kwargs):
Your_model.objects.filter(pk=instance.pk).update(model_attribute=any_value)
- [Django]-How do I use Django groups and permissions?
- [Django]-Disable migrations when running unit tests in Django 1.7
- [Django]-Dynamic choices field in Django Models