diff --git a/django/db/transaction.py b/django/db/transaction.py
index af42fd5..20dbb91 100644
--- a/django/db/transaction.py
+++ b/django/db/transaction.py
@@ -11,6 +11,7 @@ called, a commit is made.
 Managed transactions don't do those commits, but will need some kind of manual
 or implicit commits or rollbacks.
 """
+import sys
 
 try:
     import thread
@@ -20,8 +21,9 @@ try:
     from functools import wraps
 except ImportError:
     from django.utils.functional import wraps  # Python 2.4 fallback.
-from django.db import connections, DEFAULT_DB_ALIAS
+
 from django.conf import settings
+from django.db import connections, DEFAULT_DB_ALIAS
 
 class TransactionManagementError(Exception):
     """
@@ -257,31 +259,60 @@ def savepoint_commit(sid, using=None):
 # DECORATORS #
 ##############
 
-def autocommit(using=None):
-    """
-    Decorator that activates commit on save. This is Django's default behavior;
-    this decorator is useful if you globally activated transaction management in
-    your settings file and want the default behavior in some view functions.
-    """
-    def inner_autocommit(func, db=None):
-        def _autocommit(*args, **kw):
+class Transaction(object):
+    def __init__(self, entering, exiting, using):
+        self.entering = entering
+        self.exiting = exiting
+        self.using = using
+
+    def __enter__(self):
+        self.entering(self.using)
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.exiting(exc_value, self.using)
+
+    def __call__(self, func):
+        @wraps(func)
+        def inner(*args, **kwargs):
+            # Once we drop support for Python 2.4 this block should become:
+            # with self:
+            #     func(*args, **kwargs)
+            self.__enter__()
             try:
-                enter_transaction_management(managed=False, using=db)
-                managed(False, using=db)
-                return func(*args, **kw)
-            finally:
-                leave_transaction_management(using=db)
-        return wraps(func)(_autocommit)
+                res = func(*args, **kwargs)
+            except:
+                self.__exit__(*sys.exc_info())
+                raise
+            else:
+                self.__exit__(None, None, None)
+                return res
+        return inner
 
+def _transaction_func(entering, exiting, using):
     # Note that although the first argument is *called* `using`, it
     # may actually be a function; @autocommit and @autocommit('foo')
     # are both allowed forms.
     if using is None:
         using = DEFAULT_DB_ALIAS
     if callable(using):
-        return inner_autocommit(using, DEFAULT_DB_ALIAS)
-    return lambda func: inner_autocommit(func,  using)
+        return Transaction(entering, exiting, DEFAULT_DB_ALIAS)(using)
+    return Transaction(entering, exiting, using)
+
+
+def autocommit(using=None):
+    """
+    Decorator that activates commit on save. This is Django's default behavior;
+    this decorator is useful if you globally activated transaction management in
+    your settings file and want the default behavior in some view functions.
+    """
+    def entering(using):
+        enter_transaction_management(managed=False, using=using)
+        managed(False, using=using)
+
+    def exiting(exc_value, using):
+        leave_transaction_management(using=using)
 
+    return _transaction_func(entering, exiting, using)
 
 def commit_on_success(using=None):
     """
@@ -290,38 +321,23 @@ def commit_on_success(using=None):
     a rollback is made. This is one of the most common ways to do transaction
     control in Web apps.
     """
-    def inner_commit_on_success(func, db=None):
-        def _commit_on_success(*args, **kw):
-            try:
-                enter_transaction_management(using=db)
-                managed(True, using=db)
+    def entering(using):
+        enter_transaction_management(using=using)
+        managed(True, using=using)
+
+    def exiting(exc_value, using):
+        if exc_value is not None:
+            if is_dirty(using=using):
+                rollback(using=using)
+        else:
+            if is_dirty(using=using):
                 try:
-                    res = func(*args, **kw)
+                    commit(using=using)
                 except:
-                    # All exceptions must be handled here (even string ones).
-                    if is_dirty(using=db):
-                        rollback(using=db)
+                    rollback(using=using)
                     raise
-                else:
-                    if is_dirty(using=db):
-                        try:
-                            commit(using=db)
-                        except:
-                            rollback(using=db)
-                            raise
-                return res
-            finally:
-                leave_transaction_management(using=db)
-        return wraps(func)(_commit_on_success)
 
-    # Note that although the first argument is *called* `using`, it
-    # may actually be a function; @autocommit and @autocommit('foo')
-    # are both allowed forms.
-    if using is None:
-        using = DEFAULT_DB_ALIAS
-    if callable(using):
-        return inner_commit_on_success(using, DEFAULT_DB_ALIAS)
-    return lambda func: inner_commit_on_success(func, using)
+    return _transaction_func(entering, exiting, using)
 
 def commit_manually(using=None):
     """
@@ -330,22 +346,11 @@ def commit_manually(using=None):
     own -- it's up to the user to call the commit and rollback functions
     themselves.
     """
-    def inner_commit_manually(func, db=None):
-        def _commit_manually(*args, **kw):
-            try:
-                enter_transaction_management(using=db)
-                managed(True, using=db)
-                return func(*args, **kw)
-            finally:
-                leave_transaction_management(using=db)
+    def entering(using):
+        enter_transaction_management(using=using)
+        managed(True, using=using)
 
-        return wraps(func)(_commit_manually)
+    def exiting(exc_value, using):
+        leave_transaction_management(using=using)
 
-    # Note that although the first argument is *called* `using`, it
-    # may actually be a function; @autocommit and @autocommit('foo')
-    # are both allowed forms.
-    if using is None:
-        using = DEFAULT_DB_ALIAS
-    if callable(using):
-        return inner_commit_manually(using, DEFAULT_DB_ALIAS)
-    return lambda func: inner_commit_manually(func, using)
+    return _transaction_func(entering, exiting, using)
diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt
index d681c4d..d5d7eac 100644
--- a/docs/releases/1.3.txt
+++ b/docs/releases/1.3.txt
@@ -56,6 +56,19 @@ you just won't get any of the nice new unittest2 features.
 
 .. _unittest2: http://pypi.python.org/pypi/unittest2
 
+Transactions context managers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Transactions functions can now be used as a context manager, with Python 2.5
+and above.  For example:
+
+    with transaction.autocommit():
+        # ...
+
+For more information see :doc:`the transactions documentation
+</topic/db/transactions>`.
+
+
 Everything else
 ~~~~~~~~~~~~~~~
 
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index 2d99c17..4fdbb3c 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -61,6 +61,9 @@ parameter which should be the alias for a database connection for which the
 behavior applies to.  If no alias is specified then the ``"default"`` database
 is used.
 
+In addition, all of these decorators can be used as a context manager with
+Python 2.5 and above.
+
 .. note::
 
     Although the examples below use view functions as examples, these
diff --git a/tests/modeltests/transactions/tests.py b/tests/modeltests/transactions/tests.py
index be95005..9deb183 100644
--- a/tests/modeltests/transactions/tests.py
+++ b/tests/modeltests/transactions/tests.py
@@ -1,3 +1,5 @@
+import sys
+
 from django.db import connection, transaction, IntegrityError, DEFAULT_DB_ALIAS
 from django.conf import settings
 from django.test import TransactionTestCase, skipUnlessDBFeature
@@ -5,6 +7,10 @@ from django.test import TransactionTestCase, skipUnlessDBFeature
 from models import Reporter
 
 
+if sys.version_info >= (2, 5):
+    from tests_25 import TransactionContextManagerTests
+
+
 class TransactionTests(TransactionTestCase):
     def create_a_reporter_then_fail(self, first, last):
         a = Reporter(first_name=first, last_name=last)
diff --git a/tests/modeltests/transactions/tests_25.py b/tests/modeltests/transactions/tests_25.py
new file mode 100644
index 0000000..5c191f2
--- /dev/null
+++ b/tests/modeltests/transactions/tests_25.py
@@ -0,0 +1,123 @@
+from __future__ import with_statement
+
+from django.db import connection, transaction, IntegrityError
+from django.test import TransactionTestCase, skipUnlessDBFeature
+
+from models import Reporter
+
+
+class TransactionContextManagerTests(TransactionTestCase):
+    def create_reporter_and_fail(self):
+        Reporter.objects.create(first_name="Bob", last_name="Holtzman")
+        raise Exception
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_autocommit(self):
+        """
+        The default behavior is to autocommit after each save() action.
+        """
+        with self.assertRaises(Exception):
+            self.create_reporter_and_fail()
+        # The object created before the exception still exists
+        self.assertEqual(Reporter.objects.count(), 1)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_autocommit_context_manager(self):
+        """
+        The autocommit context manager works exactly the same as the default
+        behavior.
+        """
+        with self.assertRaises(Exception):
+            with transaction.autocommit():
+                self.create_reporter_and_fail()
+
+        self.assertEqual(Reporter.objects.count(), 1)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_autocommit_decorator_with_using(self):
+        """
+        The autocommit context manager also works with a using argument.
+        """
+        with self.assertRaises(Exception):
+            with transaction.autocommit():
+                self.create_reporter_and_fail()
+
+        self.assertEqual(Reporter.objects.count(), 1)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_commit_on_success(self):
+        """
+        With the commit_on_success context manager, the transaction is only
+        committed if the block doesn't throw an exception.
+        """
+        with self.assertRaises(Exception):
+            with transaction.commit_on_success():
+                self.create_reporter_and_fail()
+
+        self.assertEqual(Reporter.objects.count(), 0)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_commit_on_success_with_using(self):
+        """
+        The commit_on_success context manager also works with a using argument.
+        """
+        with self.assertRaises(Exception):
+            with transaction.commit_on_success(using="default"):
+                self.create_reporter_and_fail()
+
+        self.assertEqual(Reporter.objects.count(), 0)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_commit_on_success_succeed(self):
+        """
+        If there aren't any exceptions, the data will get saved.
+        """
+        Reporter.objects.create(first_name="Alice", last_name="Smith")
+        with transaction.commit_on_success():
+            Reporter.objects.filter(first_name="Alice").delete()
+
+        self.assertQuerysetEqual(Reporter.objects.all(), [])
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_manually_managed(self):
+        """
+        You can manually manage transactions if you really want to, but you
+        have to remember to commit/rollback.
+        """
+        with transaction.commit_manually():
+            Reporter.objects.create(first_name="Libby", last_name="Holtzman")
+            transaction.commit()
+        self.assertEqual(Reporter.objects.count(), 1)
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_manually_managed_mistake(self):
+        """
+        If you forget, you'll get bad errors.
+        """
+        with self.assertRaises(transaction.TransactionManagementError):
+            with transaction.commit_manually():
+                Reporter.objects.create(first_name="Scott", last_name="Browning")
+
+    @skipUnlessDBFeature('supports_transactions')
+    def test_manually_managed_with_using(self):
+        """
+        The commit_manually function also works with a using argument.
+        """
+        with self.assertRaises(transaction.TransactionManagementError):
+            with transaction.commit_manually(using="default"):
+                Reporter.objects.create(first_name="Walter", last_name="Cronkite")
+
+    @skipUnlessDBFeature('requires_rollback_on_dirty_transaction')
+    def test_bad_sql(self):
+        """
+        Regression for #11900: If a block wrapped by commit_on_success
+        writes a transaction that can't be committed, that transaction should
+        be rolled back. The bug is only visible using the psycopg2 backend,
+        though the fix is generally a good idea.
+        """
+        with self.assertRaises(IntegrityError):
+            with transaction.commit_on_success():
+                cursor = connection.cursor()
+                cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
+                transaction.set_dirty()
+        transaction.rollback()
