9👍
Not really. The signals have nothing to do with the db transaction success or failure, but with the save method itself – before the call you have the pre_save signal fired and after the call you have the post_save signal fired.
There are 2 approaches here:
- you are going to inspect the instance in the post_save method and decide that the model was saved successfully or not; simplest way to do that: in the save method, after the transaction executed successfully, annotate your instance with a flag, say
instance.saved_successfully = True
, which you will test in the post_save handler. - you are going to ditch the post_save signal and create a custom signal for yourself, which you will trigger after the transaction ran successfully.
Makes sense?
P.S.
If you strictly need to bind to the transaction commit signal, have a look over this package: https://django-transaction-hooks.readthedocs.org/en/latest/; it looks like the functionality is integrated in Django 1.9a.
46👍
I think the simplest way is to use transaction.on_commit(). Here’s an example using the models.Model subclass Photo
that will only talk to Elasticsearch once the current transaction is over:
from django.db import transaction
from django.db.models.signals import post_save
@receiver(post_save, sender=Photo)
def save_photo(**kwargs):
transaction.on_commit(lambda: talk_to_elasticsearch(kwargs['instance']))
Note that if the transaction.on_commit()
gets executed while not in an active transaction, it will run right away.
- [Django]-Best practice for Django project working directory structure
- [Django]-Understanding ManyToMany fields in Django with a through model
- [Django]-How to use permission_required decorators on django class-based views
7👍
I was having serious issues with django’s admin not allowing post_save transactions on parent objects when they had inline children being modified.
This was my solution to an error complaining about conducting queries in the middle of an atomic block:
def on_user_post_save_impl(user):
do_something_to_the_user(user)
def on_user_post_save(sender, instance, **kwargs):
if not transaction.get_connection().in_atomic_block:
on_user_post_save_impl(instance)
else:
transaction.on_commit(lambda: on_user_post_save_impl(instance))
- [Django]-Get absolute file path of FileField of a model instance in Django
- [Django]-Django Rest Framework Token Authentication
- [Django]-Can I access constants in settings.py from templates in Django?
1👍
We are using this little nugget:
def atomic_post_save(sender, instance, **kwargs):
if hasattr(instance, "atomic_post_save") and transaction.get_connection().in_atomic_block:
transaction.on_commit(lambda: instance.atomic_post_save(sender, instance=instance, **kwargs))
post_save.connect(atomic_post_save)
Then we simply define a atomic_post_save
method on any model we like:
class MyModel(Model):
def atomic_post_save(self, sender, created, **kwargs):
talk_to_elasticsearch(self)
Two things to notice:
- We only call
atomic_post_save
when inside a transaction. - It’s too late in the flow to send messages and have them included in the current request from inside
atomic_post_save
.
- [Django]-Dynamically adding a form to a Django formset
- [Django]-Django: order_by multiple fields
- [Django]-Django nested transactions – “with transaction.atomic()”