开发者

django rollback transaction in save method

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 incremente开发者_如何学编程d 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?


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.


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)


I think Mike DeSimone's answer is the right one.

Concerning the database, depending on what MySQL version you use (if you use it), it might be that tables your database use MyISAM engine, that does not support transactions.

To check it just run in mysql shell:

SELECT TABLE_NAME, 
    ENGINE 
    FROM information_schema.TABLES 
    where TABLE_SCHEMA = 'your_db_name' ;

You can alter your tables to InnoDB and set default_storage_engine to innodb in MySQL config. (Details here: http://parasjain.net/2010/06/08/how-to-switch-to-innodb-database-in-mysql/.

After that transactions should work. It is better to use Postgres, but if you want to use MySQL/InnoDB, then probably you'll need a workaround for loading fixtures with forward references (the BugFix is already present in Django Trunk, and I've also ported it to Django 1.3.1, see Django 1.3.1.1 on Github).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜