tags:

views:

61

answers:

2

I have the following piece of code overriding the save method of a model:

@transaction.commit_on_success
def save(self, *args, **kwargs):

    try:
        transaction.commit()
        self.qa.vote_down_count += 1
        self.qa.save()

        super(self.__class__, self).save(*args, **kwargs)

    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

The expected behavior would be: self.qa attribute vote_down_count is incremented by one, but if any exception occurs in the super(self) save method the transaction rollbacks (that means the self.qa.vote_down_count += 1 is not committed in the database).

The actual behavior is: self.qa.vote_down_count += 1 is committed to database even if an IntegrityError exception raises from super(self) save.

Any thoughs?

+1  A: 

Why not simply do:

@transaction.commit_manually
def save(self, *args, **kwargs):
    try:
        super(self.__class__, self).save(*args, **kwargs)
        self.qa.vote_down_count += 1
        self.qa.save()
    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

This is how the docs imply to do it, though they say to do this in your view function, so you might not need the @transaction.commit_manually on the save() method, instead putting it on the view.

Mike DeSimone
Change the orders of the operations doesn't solve the problem in my opinion, it doesn't prevent the error to happen, there are other cases when self.qa.save() must be done before. commit_amually decorator doesn't change the behavior.
will.i.am
A: 

Try to use savepoints. Something like this:

def save(self, *args, **kwargs):

try:
    sid = transaction.savepoint()
    self.qa.vote_down_count += 1
    self.qa.save()

    super(self.__class__, self).save(*args, **kwargs)

except:
    transaction.rollback(sid)
    raise
else:
    transaction.commit(sid)
Aldarund
I suppose you mean savepoint_rollback and savepoint_commit, it doesn't work either.
will.i.am