diff -r ed4d2e6277b2 django/core/management/commands/loaddata.py
--- a/django/core/management/commands/loaddata.py	Thu Aug 07 22:23:54 2008 +0100
+++ b/django/core/management/commands/loaddata.py	Thu Aug 07 23:11:58 2008 +0100
@@ -28,6 +28,7 @@
 
         verbosity = int(options.get('verbosity', 1))
         show_traceback = options.get('traceback', False)
+        no_commit = options.get('no_commit', False)
 
         # Keep a count of the installed objects and fixtures
         fixture_count = 0
@@ -44,9 +45,10 @@
 
         # Start transaction management. All fixtures are installed in a
         # single transaction to ensure that all references are resolved.
-        transaction.commit_unless_managed()
-        transaction.enter_transaction_management()
-        transaction.managed(True)
+        if not no_commit:
+            transaction.commit_unless_managed()
+            transaction.enter_transaction_management()
+            transaction.managed(True)
 
         app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') for app in get_apps()]
         for fixture_label in fixture_labels:
@@ -133,7 +135,7 @@
                                 (format, fixture_name, humanize(fixture_dir))
 
 
-        # If any of the fixtures we loaded contain 0 objects, assume that an 
+        # If any of the fixtures we loaded contain 0 objects, assume that an
         # error was encountered during fixture loading.
         if 0 in objects_per_fixture:
             sys.stderr.write(
@@ -142,8 +144,8 @@
             transaction.rollback()
             transaction.leave_transaction_management()
             return
-            
-        # If we found even one object in a fixture, we need to reset the 
+
+        # If we found even one object in a fixture, we need to reset the
         # database sequences.
         if object_count > 0:
             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
@@ -152,9 +154,10 @@
                     print "Resetting sequences"
                 for line in sequence_sql:
                     cursor.execute(line)
-            
-        transaction.commit()
-        transaction.leave_transaction_management()
+
+        if not no_commit:
+            transaction.commit()
+            transaction.leave_transaction_management()
 
         if object_count == 0:
             if verbosity > 1:
@@ -162,9 +165,10 @@
         else:
             if verbosity > 0:
                 print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count)
-                
+
         # Close the DB connection. This is required as a workaround for an
         # edge case in MySQL: if the same connection is used to
         # create tables, load data, and query, the query can return
         # incorrect results. See Django #7572, MySQL #37735.
-        connection.close()
+        if not no_commit:
+            connection.close()
diff -r ed4d2e6277b2 django/test/__init__.py
--- a/django/test/__init__.py	Thu Aug 07 22:23:54 2008 +0100
+++ b/django/test/__init__.py	Thu Aug 07 23:11:58 2008 +0100
@@ -3,4 +3,4 @@
 """
 
 from django.test.client import Client
-from django.test.testcases import TestCase
+from django.test.testcases import TestCase, TransactionTestCase
diff -r ed4d2e6277b2 django/test/testcases.py
--- a/django/test/testcases.py	Thu Aug 07 22:23:54 2008 +0100
+++ b/django/test/testcases.py	Thu Aug 07 23:11:58 2008 +0100
@@ -55,7 +55,7 @@
         """Tries to do a 'xml-comparision' of want and got.  Plain string
         comparision doesn't always work because, for example, attribute
         ordering should not be important.
-        
+
         Based on http://codespeak.net/svn/lxml/trunk/src/lxml/doctestcompare.py
         """
         _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
@@ -102,7 +102,7 @@
             wrapper = '<root>%s</root>'
             want = wrapper % want
             got = wrapper % got
-            
+
         # Parse the want and got strings, and compare the parsings.
         try:
             want_root = parseString(want).firstChild
@@ -169,27 +169,44 @@
         # side effects on other tests.
         transaction.rollback_unless_managed()
 
-class TestCase(unittest.TestCase):
+    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 TransactionTestCase(unittest.TestCase):
     def _pre_setup(self):
         """Performs any pre-test setup. This includes:
 
             * Flushing the database.
-            * If the Test Case class has a 'fixtures' member, installing the 
+            * If the Test Case class has a 'fixtures' member, installing the
               named fixtures.
             * If the Test Case class has a 'urls' member, replace the
               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 +223,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 +238,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 +378,23 @@
         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.
+    """
+    def _fixture_setup(self):
+        transaction.enter_transaction_management()
+        transaction.managed(True)
+
+        if hasattr(self, 'fixtures'):
+            call_command('loaddata', *self.fixtures, **{
+                                                        'verbosity': 0,
+                                                        'no_commit': True
+                                                        })
+
+    def _fixture_teardown(self):
+        transaction.rollback()
+        transaction.leave_transaction_management()
