Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - Rollback save with transaction atomic

I am trying to create a view where I save an object but I'd like to undo that save if some exception is raised. This is what I tried:

class MyView(View):      @transaction.atomic     def post(self, request, *args, **kwargs):         try:             some_object = SomeModel(...)             some_object.save()              if something:                 raise exception.NotAcceptable()                 # When the workflow comes into this condition, I think the previous save should be undone                 # What am I missing?          except exception.NotAcceptable, e:             # do something 

What am I doing wrong? even when the exception is raised some_object is still in Database.

like image 729
Gocht Avatar asked Jan 11 '16 20:01

Gocht


People also ask

How do I rollback in Django?

If an exception occurs, Django will perform the rollback when exiting the first parent block with a savepoint if there is one, and the outermost block otherwise. Atomicity is still guaranteed by the outer transaction. This option should only be used if the overhead of savepoints is noticeable.

Are transactions atomic?

A transaction guarantees atomicity.

What is atomic transaction in SOA?

An atomic transaction is a single, irreducible component of a classic transaction, such as making a purchase. WS-AT ensures that if a single atomic transaction fails, the whole transaction fails: A partial transaction cannot take place.

How does Django handle concurrency?

When you run multiple workers of your Django application, you will run into concurrency issues when the same queryset is updated by different processes at the same time. To prevent this, use select_for_update inside a transaction block to fetch your queryset so that it is locked until the transaction is completed.


2 Answers

Atomicity Documentation

To summarize, @transaction.atomic will execute a transaction on the database if your view produces a response without errors. Because you're catching the exception yourself, it appears to Django that your view executed just fine.

If you catch the exception, you need to handle it yourself: Controlling Transactions

If you need to produce a proper json response in the event of failure:

from django.db import SomeError, transaction  def viewfunc(request):     do_something()      try:         with transaction.atomic():             thing_that_might_fail()     except SomeError:         handle_exception()      render_response() 
like image 85
jlucier Avatar answered Sep 20 '22 16:09

jlucier


However, if an exception happens in a function decorated with transaction.atomic, then you don't have anything to do, it'll rollback automatically to the savepoint created by the decorator before running the your function, as documented:

atomic allows us to create a block of code within which the atomicity on the database is guaranteed. 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.

If the exception is catched in an except block, then it should be re-raised for atomic to catch it and do the rollback, ie.:

    try:         some_object = SomeModel(...)         some_object.save()          if something:             raise exception.NotAcceptable()             # When the workflow comes into this condition, I think the previous save should be undome             # Whant am I missing?      except exception.NotAcceptable, e:         # do something         raise  # re-raise the exception to make transaction.atomic rollback 

Also, if you want more control, you can rollback manually to previously set savepoint, ie.:

class MyView(View):     def post(self, request, *args, **kwargs):         sid = transaction.savepoint()         some_object = SomeModel(...)         some_object.save()          if something:             transaction.savepoint_rollback(sid)         else:             try:                 # In worst case scenario, this might fail too                 transaction.savepoint_commit(sid)             except IntegrityError:                 transaction.savepoint_rollback(sid) 
like image 45
jpic Avatar answered Sep 19 '22 16:09

jpic