Sam
Sam

Reputation: 2084

Django - operate on database within transaction.atomic block after raising error

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

Answers (1)

Kevin Christopher Henry
Kevin Christopher Henry

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

Related Questions