如何在4xx或5xx HTTP状态代码上进行django模型原子性回滚

时间:2016-02-28 22:37:38

标签: python django python-2.7 django-models transactions

据我所知,django atomicity of the-box仅在异常被抛出时回滚事务。但是,有一些例外,我在我的脚本中捕获,对于那些例外,我正在为用户生成一个很好的HTTP响应和一些有意义的响应内容 - 但我总是确保在这种情况下的HTTP响应是用适当的4xx发送的或5xx HTTP状态代码。当发生这样的HTTP响应时,我希望django回滚它到目前为止可能执行的所有数据库查询。但是,django atomicity似乎并不基于正在发送的HTTP状态代码来执行,它仅基于抛出给用户的异常来起作用。有关如何在django 1.8 python 2.7上解决此问题的任何建议?

1 个答案:

答案 0 :(得分:1)

尝试创建可以执行此操作的自定义中间件。这是一个based on the old TransactionMiddleware的示例(这是未经测试的):

from django.db import transaction

class StatusCodeTransactionMiddleware(object):
    """
    Rolls back the current transaction for all responses with 4xx or 5xx status
    codes.
    """

    def process_request(self, request):
        """Enters transaction management"""
        transaction.enter_transaction_management()

    def process_response(self, request, response):
        """Commits and leaves transaction management."""
        if response.status_code >= 400:
            if transaction.is_dirty():
                # This rollback might fail because of network failure for
                # example. If rollback isn't possible it is impossible to
                # clean the connection's state. So leave the connection in
                # dirty state and let request_finished signal deal with
                # cleaning the connection.
                transaction.rollback()
            transaction.leave_transaction_management()
        else:
            if not transaction.get_autocommit():
                if transaction.is_dirty():
                    # Note: it is possible that the commit fails. If the
                    # reason is closed connection or some similar reason,
                    # then there is little hope to proceed nicely.
                    # However, in some cases ( deferred foreign key checks
                    # for example) it is still possible to rollback().
                    try:
                        transaction.commit()
                    except Exception:
                        # If the rollback fails, the transaction state will
                        # be messed up. It doesn't matter, the connection
                        # will be set to clean state after the request
                        # finishes. And, we can't clean the state here
                        # properly even if we wanted to, the connection is
                        # in transaction but we can't rollback...
                        transaction.rollback()
                        transaction.leave_transaction_management()
                        raise
                transaction.leave_transaction_management()
        return response

将它放在你的MIDDLEWARE_CLASSES中,如此:

MIDDLEWARE_CLASSES = (
    "myapp.middleware.StatusCodeTransactionMiddleware",
    # Other middleware...
)