We are trying to migrate from commit_manually
to atomic
so we can upgrade Django to at least 1.8 in a legacy project. In most of cases we need to do something like that:
with transaction.atomic():
obj = Entity.objects.select_for_update().get(pk=pk)
try:
obj.do_something()
obj.set_some_status()
obj.save()
except SomeException:
obj.set_failed_flag()
obj.save()
raise
becuase the callee needs this exception information to continue with the certain flow. But in this case the transaction/savepoint will be rolled back and that's not what we want since we want obj.set_failed_flag()
to be committed. Also it seems logical to set it inside the same atomic block since we already have a locked row for this object.
Any ideas/patterns? Thanks in advance!
P.S. It was so easy with old manual transaction management!
P.P.S. We use exceptions also for "early-exit" and moving to some flags etc. would bring a log of mess and I personally would love to avoid it.
Assuming that SomeException
isn't a database exception, you can just save it and raise it outside the atomic block:
with transaction.atomic():
obj = Entity.objects.select_for_update().get(pk=pk)
try:
obj.do_something()
obj.set_some_status()
except SomeException as e:
obj.set_failed_flag()
exception = e
else:
exception = None
obj.save()
if exception:
raise exception
If you find this too verbose and need to do it frequently, you might be able to write a context manager that acts as a proxy for transaction.atomic()
but doesn't trigger a rollback in certain cases.
Finally, note that Django still has manual transaction management.
Besides the answer already posted, in case of having nested methods where you start the atomic block somewhere higher in the chain, it helps to use the following:
transaction.on_commit(lambda: method_that_raises_exception())
This way the exception is raised after the transaction has been committed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With