Index: django/test/client.py
===================================================================
--- django/test/client.py	(revision 9640)
+++ django/test/client.py	(working copy)
@@ -19,6 +19,7 @@
 from django.utils.encoding import smart_str
 from django.utils.http import urlencode
 from django.utils.itercompat import is_iterable
+from django.db import transaction
 
 BOUNDARY = 'BoUnDaRyStRiNg'
 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
@@ -61,8 +62,12 @@
 
         signals.request_started.send(sender=self.__class__)
         try:
+            transaction.enter_transaction_management()
+            transaction.managed(True)         
             request = WSGIRequest(environ)
             response = self.get_response(request)
+            transaction.commit()
+            transaction.leave_transaction_management()
 
             # Apply response middleware.
             for middleware_method in self._response_middleware:
@@ -172,6 +177,10 @@
         Obtains the current session variables.
         """
         if 'django.contrib.sessions' in settings.INSTALLED_APPS:
+            # If a session db change hangs in a transaction, commit,
+            # just to be sure.
+            if transaction.is_dirty():
+                transaction.commit()
             engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
             cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
             if cookie:
Index: django/test/testcases.py
===================================================================
--- django/test/testcases.py	(revision 9640)
+++ django/test/testcases.py	(working copy)
@@ -8,6 +8,7 @@
 from django.core.management import call_command
 from django.core.urlresolvers import clear_url_caches
 from django.db import transaction
+from django.db.models.signals import post_save
 from django.http import QueryDict
 from django.test import _doctest as doctest
 from django.test.client import Client
@@ -168,8 +169,19 @@
         # Rollback, in case of database errors. Otherwise they'd have
         # side effects on other tests.
         transaction.rollback_unless_managed()
+        
+    def run(self, test, compileflags=None, out=None, clear_globs=True):
+        """
+        Wraps the parent run() and encloses it in a transaction.
+        """
+        transaction.enter_transaction_management()
+        transaction.managed(True)
+        result = doctest.DocTestRunner.run(self, test, compileflags, out, clear_globs)
+        transaction.rollback()
+        transaction.leave_transaction_management()
+        return result
 
-class TestCase(unittest.TestCase):
+class TransactionTestCase(unittest.TestCase):
     def _pre_setup(self):
         """Performs any pre-test setup. This includes:
 
@@ -180,16 +192,22 @@
               ROOT_URLCONF with it.
             * Clearing the mail test outbox.
         """
+        self._fixture_setup()
+        self._urlconf_setup()
+        mail.outbox = []
+
+    def _fixture_setup(self):
         call_command('flush', verbosity=0, interactive=False)
         if hasattr(self, 'fixtures'):
             # We have to use this slightly awkward syntax due to the fact
             # that we're using *args and **kwargs together.
             call_command('loaddata', *self.fixtures, **{'verbosity': 0})
+
+    def _urlconf_setup(self):
         if hasattr(self, 'urls'):
             self._old_root_urlconf = settings.ROOT_URLCONF
             settings.ROOT_URLCONF = self.urls
             clear_url_caches()
-        mail.outbox = []
 
     def __call__(self, result=None):
         """
@@ -206,7 +224,7 @@
             import sys
             result.addError(self, sys.exc_info())
             return
-        super(TestCase, self).__call__(result)
+        super(TransactionTestCase, self).__call__(result)        
         try:
             self._post_teardown()
         except (KeyboardInterrupt, SystemExit):
@@ -221,6 +239,13 @@
 
             * Putting back the original ROOT_URLCONF if it was changed.
         """
+        self._fixture_teardown()
+        self._urlconf_teardown()
+
+    def _fixture_teardown(self):
+        pass
+
+    def _urlconf_teardown(self):        
         if hasattr(self, '_old_root_urlconf'):
             settings.ROOT_URLCONF = self._old_root_urlconf
             clear_url_caches()
@@ -354,3 +379,49 @@
         self.failIf(template_name in template_names,
             (u"Template '%s' was used unexpectedly in rendering the"
              u" response") % template_name)
+
+class TestCase(TransactionTestCase):
+    """
+    Does basically the same as TransactionTestCase, but surrounds every test
+    with a transaction. You have to use TransactionTestCase, if you need
+    transaction management inside a test.
+    """
+
+    # has the db been changed during the test
+    db_was_changed = False
+
+    def _fixture_setup(self):
+        transaction.enter_transaction_management()
+        transaction.managed(True)
+
+        # whenever a save occured, the db must be dirty
+        post_save.connect(self._set_db_was_changed)
+        # this seems more elegant than patching ClientHandler
+        #request_started.connect(self._do_commit)
+        #request_finished.connect(self._do_commit)
+
+        if hasattr(self, 'fixtures'):
+            call_command('loaddata', *self.fixtures, **{
+                                                        'verbosity': 0,
+                                                        'commit': False
+                                                        })
+            # TODO: find out, if loaddata does emit a post_save signal
+            self._set_db_was_changed()
+
+    def _fixture_teardown(self):
+        # If the transaction is not dirty, but the DB was changed,
+        # a commit must have happened, so flush instead of rollback.
+        # This currently doesn't catch the following case:
+        # Inside a test a commit happens and after that more data is changed.
+        if not transaction.is_dirty() and self.db_was_changed:
+            transaction.leave_transaction_management()
+            call_command('flush', verbosity=0, interactive=False)
+        else:
+            transaction.rollback()
+            transaction.leave_transaction_management()
+
+    def _set_db_was_changed(self, *args, **kwargs):
+        self.db_was_changed = True
+
+    def _do_commit(self, *args, **kwargs):
+        transaction.commit()
Index: django/test/__init__.py
===================================================================
--- django/test/__init__.py	(revision 9640)
+++ django/test/__init__.py	(working copy)
@@ -3,4 +3,4 @@
 """
 
 from django.test.client import Client
-from django.test.testcases import TestCase
+from django.test.testcases import TestCase, TransactionTestCase
Index: tests/regressiontests/admin_views/tests.py
===================================================================
--- tests/regressiontests/admin_views/tests.py	(revision 9640)
+++ tests/regressiontests/admin_views/tests.py	(working copy)
@@ -1,6 +1,6 @@
 # coding: utf-8
 
-from django.test import TestCase
+from django.test import TestCase, TransactionTestCase
 from django.contrib.auth.models import User, Permission
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.admin.models import LogEntry
@@ -173,9 +173,14 @@
     ct = ContentType.objects.get_for_model(Model)
     return Permission.objects.get(content_type=ct, codename=perm)
 
-class AdminViewPermissionsTest(TestCase):
-    """Tests for Admin Views Permissions."""
+class AdminViewPermissionsTest(TransactionTestCase):
+    """
+    Tests for Admin Views Permissions.
 
+    We need TransactionTestCase here, because some data is lodaed manually
+    via the ORM, not via fixtures and test.Client is used.
+    """
+
     fixtures = ['admin-views-users.xml']
 
     def setUp(self):
@@ -562,8 +567,8 @@
         should_contain = """<h1>Change model with string primary key</h1>"""
         self.assertContains(response, should_contain)
         
-
-class SecureViewTest(TestCase):
+       
+class SecureViewTest(TransactionTestCase):
     fixtures = ['admin-views-users.xml']
 
     def setUp(self):
