Reputation: 2084
I would like to perform an action on my database within a transaction.atomic() block, even in the event that an error is raised. Here is some sample code to demonstrate my issue:
Sample code
# Outer try block
try:
# Enclose in atomic transaction for database rollbacks
with transaction.atomic():
# If this line fails, all database updates within the outer try: block should be rolled back
u = UU.create(email='[email protected]')
# Inner try block
try:
cc = CC.objects.get(id=1)
perform_action(cc)
# If this exception triggers, the 'cc' object should be deleted, but all other database updates within the outer try: block should be rolled back
except:
cc.delete()
raise
# If this line fails, all database updates within the outer try: block should be rolled back
u = UU.create(email='[email protected]')
# If any exception triggers, this error should be printed
except:
print("Error occured.")
If an error occurs in my inner try:
block, I want the cc
object to be deleted, but all other database transactions within the outer try:
block to be rolled back. However, as the code stands now, the cc.delete()
transaction will be rolled back if any error occurs within the inner try:
block.
Any suggestions?
Upvotes: 4
Views: 1871
Reputation: 49092
You can't keep just one part of a database transaction, and you can't keep an inner transaction while rolling back the outer transaction.
Instead, you can signal that specific error state with a custom exception, and then do your additional processing when you catch it, after the rollback. Something like:
class BadCCException(Exception):
def __init__(self, badid):
super().__init__()
self.badid = badid
try:
with transaction.atomic():
u = UU.create(email='[email protected]')
try:
cc = CC.objects.get(id=1)
perform_action(cc)
except Exception as e:
raise BadCCException(1) from e
u = UU.create(email='[email protected]')
except BadCCException as e:
CC.objects.filter(id=e.badid).delete()
print("Error occured.")
except:
print("Error occured.")
Upvotes: 4