60

I would like to know if I have something like this:

def functionA():
    with transaction.atomic():
        #save something
        functionB()

def functionB():
    with transaction.atomic():
        #save another thing

Someone knows what will happen? If functionB fails, functionA will rollback too?

Thank you!

Lara
  • 2,170
  • 6
  • 22
  • 43

2 Answers2

69

Yes, it will. Regardless of nesting, if an atomic block is exited by an exception it will roll back:

If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.

Note also that an exception in an outer block will cause the inner block to roll back, and that an exception in an inner block can be caught to prevent the outer block from rolling back. The documentation addresses these issues. (Or see here for a more comprehensive follow-up question on nested transactions).

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
  • "an exception in an inner block can be caught to prevent the outer block from rolling back" this part seems to be conflict with the practice "Avoid catching exceptions inside atomic!" https://docs.djangoproject.com/en/3.0/topics/db/transactions/#controlling-transactions-explicitly – Jzou Jan 21 '20 at 18:31
  • 7
    @JialinZou: There's no conflict. If you read that section in the documentation you will see a reference to the "correct way" to catch database errors, the example of which involves catching an exception in an inner block. What they are trying to warn you against is catching an exception and trying to continue on with the transaction in a broken state. The right way is to wrap the inner code with `atomic()` to ensure that the broken database operations are rolled back before continuing. – Kevin Christopher Henry Jan 21 '20 at 20:06
  • Thanks for clarifying! This is very helpful. – Jzou Jan 21 '20 at 21:30
0
with transaction.atomic(): # Outer atomic, start a new transaction
    transaction.on_commit(foo)
    try:
        with transaction.atomic(): # Inner atomic block, create a savepoint
            transaction.on_commit(bar)
            raise SomeError() # Raising an exception - abort the savepoint
    except SomeError:
        pass
# foo() will be called, but not bar()
Jorrick Sleijster
  • 935
  • 1
  • 9
  • 22
RAHUL PAL
  • 23
  • 4