diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index 34f3543..4451a77 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -1,4 +1,3 @@
-import sys
 import os
 import gzip
 import zipfile
@@ -6,7 +5,7 @@ from optparse import make_option
 
 from django.conf import settings
 from django.core import serializers
-from django.core.management.base import BaseCommand
+from django.core.management.base import BaseCommand, CommandError
 from django.core.management.color import no_style
 from django.db import connections, router, transaction, DEFAULT_DB_ALIAS
 from django.db.models import get_apps
@@ -18,6 +17,136 @@ try:
 except ImportError:
     has_bz2 = False
 
+
+class SingleZipReader(zipfile.ZipFile):
+    def __init__(self, *args, **kwargs):
+        zipfile.ZipFile.__init__(self, *args, **kwargs)
+        if settings.DEBUG:
+            assert len(self.namelist()) == 1, "Zip-compressed fixtures must contain only one file."
+
+    def read(self):
+        return zipfile.ZipFile.read(self, self.namelist()[0])
+
+def humanize(dirname):
+    if dirname:
+        return "'%s'" % dirname
+    return 'absolute path'
+
+def find_fixture_data(fixture_labels, verbosity, using, stdout):
+    app_module_paths = []
+    for app in get_apps():
+        if hasattr(app, '__path__'):
+            # It's a 'models/' subpackage
+            app_module_paths.extend(app.__path__)
+        else:
+            # It's a models.py module
+            app_module_paths.append(app.__file__)
+    app_fixtures = [
+        os.path.join(os.path.dirname(path), 'fixtures')
+        for path in app_module_paths
+    ]
+
+    compression_types = {
+        None:   file,
+        'gz':   gzip.GzipFile,
+        'zip':  SingleZipReader
+    }
+    if has_bz2:
+        compression_types['bz2'] = bz2.BZ2File
+
+    objs = []
+    models = set()
+    fixture_count = 0
+    found_object_count = 0
+    fixture_object_count = 0
+
+    for fixture_label in fixture_labels:
+        parts = fixture_label.split('.')
+
+        if len(parts) > 1 and parts[-1] in compression_types:
+            compression_formats = [parts[-1]]
+            parts = parts[:-1]
+        else:
+            compression_formats = compression_types.keys()
+
+        if len(parts) == 1:
+            fixture_name = parts[0]
+            formats = serializers.get_public_serializer_formats()
+        else:
+            fixture_name, format = '.'.join(parts[:-1]), parts[-1]
+            if format in serializers.get_public_serializer_formats():
+                formats = [format]
+            else:
+                formats = []
+
+        if formats:
+            if verbosity >= 2:
+                stdout.write("Loading '%s' fixtures...\n" % fixture_name)
+        else:
+            raise CommandError("Problem installing fixture '%s': %s is not a "
+                "known serialization format." % (fixture_name, format))
+
+        if os.path.isabs(fixture_name):
+            fixture_dirs = [fixture_name]
+        else:
+            fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
+
+        for fixture_dir in fixture_dirs:
+            if verbosity >= 2:
+                stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
+
+            label_found = False
+            for combo in product([using, None], formats, compression_formats):
+                database, format, compression_format = combo
+                file_name = '.'.join(
+                    p for p in [
+                        fixture_name, database, format, compression_format
+                    ]
+                    if p
+                )
+
+                if verbosity >= 3:
+                    stdout.write("Trying %s for %s fixture '%s'...\n" % \
+                        (humanize(fixture_dir), file_name, fixture_name))
+                full_path = os.path.join(fixture_dir, file_name)
+                open_method = compression_types[compression_format]
+                try:
+                    fixture = open_method(full_path, 'r')
+                except IOError:
+                    if verbosity >= 2:
+                        stdout.write("No %s fixture '%s' in %s.\n" % \
+                            (format, fixture_name, humanize(fixture_dir)))
+                    continue
+
+                if label_found:
+                    fixture.close()
+                    raise CommandError("Multiple fixtures named '%s' in %s."
+                        " Aborting" % (fixture_name, humanize(fixture_dir)))
+                fixture_count += 1
+                objects_in_fixture = 0
+                found_objects_in_fixture = 0
+                if verbosity >= 2:
+                    stdout.write("Installing %s fixture '%s' from %s.\n" % \
+                        (format, fixture_name, humanize(fixture_dir)))
+                objects = serializers.deserialize(format, fixture, using=using)
+                for obj in objects:
+                    objects_in_fixture += 1
+                    if router.allow_syncdb(using, obj.object.__class__):
+                        found_objects_in_fixture += 1
+                        models.add(obj.object.__class__)
+                        objs.append(obj)
+                found_object_count += found_objects_in_fixture
+                fixture_object_count += objects_in_fixture
+                label_found = True
+                fixture.close()
+
+                # If the fixture we loaded contains 0 objects, assume that an
+                # error was encountered during fixture loading.
+                if objects_in_fixture == 0:
+                    raise CommandError("No fixture data found for '%s'. "
+                        "(File format may be invalid.)" % fixture_name)
+    return objs, models, fixture_count, fixture_object_count
+
 class Command(BaseCommand):
     help = 'Installs the named fixture(s) in the database.'
     args = "fixture [fixture ...]"
@@ -45,14 +174,6 @@ class Command(BaseCommand):
         # the transaction in place when loaddata was invoked.
         commit = options.get('commit', True)
 
-        # Keep a count of the installed objects and fixtures
-        fixture_count = 0
-        loaded_object_count = 0
-        fixture_object_count = 0
-        models = set()
-
-        humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
-
         # Get a cursor (even though we don't need one yet). This has
         # the side effect of initializing the test database (if
         # it isn't already initialized).
@@ -65,153 +186,26 @@ class Command(BaseCommand):
             transaction.enter_transaction_management(using=using)
             transaction.managed(True, using=using)
 
-        class SingleZipReader(zipfile.ZipFile):
-            def __init__(self, *args, **kwargs):
-                zipfile.ZipFile.__init__(self, *args, **kwargs)
-                if settings.DEBUG:
-                    assert len(self.namelist()) == 1, "Zip-compressed fixtures must contain only one file."
-            def read(self):
-                return zipfile.ZipFile.read(self, self.namelist()[0])
-
-        compression_types = {
-            None:   file,
-            'gz':   gzip.GzipFile,
-            'zip':  SingleZipReader
-        }
-        if has_bz2:
-            compression_types['bz2'] = bz2.BZ2File
-
-        app_module_paths = []
-        for app in get_apps():
-            if hasattr(app, '__path__'):
-                # It's a 'models/' subpackage
-                for path in app.__path__:
-                    app_module_paths.append(path)
-            else:
-                # It's a models.py module
-                app_module_paths.append(app.__file__)
-
-        app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths]
-        for fixture_label in fixture_labels:
-            parts = fixture_label.split('.')
-
-            if len(parts) > 1 and parts[-1] in compression_types:
-                compression_formats = [parts[-1]]
-                parts = parts[:-1]
-            else:
-                compression_formats = compression_types.keys()
-
-            if len(parts) == 1:
-                fixture_name = parts[0]
-                formats = serializers.get_public_serializer_formats()
-            else:
-                fixture_name, format = '.'.join(parts[:-1]), parts[-1]
-                if format in serializers.get_public_serializer_formats():
-                    formats = [format]
-                else:
-                    formats = []
-
-            if formats:
-                if verbosity >= 2:
-                    self.stdout.write("Loading '%s' fixtures...\n" % fixture_name)
-            else:
-                self.stderr.write(
-                    self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" %
-                        (fixture_name, format)))
-                if commit:
-                    transaction.rollback(using=using)
-                    transaction.leave_transaction_management(using=using)
-                return
-
-            if os.path.isabs(fixture_name):
-                fixture_dirs = [fixture_name]
-            else:
-                fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
-
-            for fixture_dir in fixture_dirs:
-                if verbosity >= 2:
-                    self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
-
-                label_found = False
-                for combo in product([using, None], formats, compression_formats):
-                    database, format, compression_format = combo
-                    file_name = '.'.join(
-                        p for p in [
-                            fixture_name, database, format, compression_format
-                        ]
-                        if p
-                    )
-
-                    if verbosity >= 3:
-                        self.stdout.write("Trying %s for %s fixture '%s'...\n" % \
-                            (humanize(fixture_dir), file_name, fixture_name))
-                    full_path = os.path.join(fixture_dir, file_name)
-                    open_method = compression_types[compression_format]
-                    try:
-                        fixture = open_method(full_path, 'r')
-                        if label_found:
-                            fixture.close()
-                            self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" %
-                                (fixture_name, humanize(fixture_dir))))
-                            if commit:
-                                transaction.rollback(using=using)
-                                transaction.leave_transaction_management(using=using)
-                            return
-                        else:
-                            fixture_count += 1
-                            objects_in_fixture = 0
-                            loaded_objects_in_fixture = 0
-                            if verbosity >= 2:
-                                self.stdout.write("Installing %s fixture '%s' from %s.\n" % \
-                                    (format, fixture_name, humanize(fixture_dir)))
-                            try:
-                                objects = serializers.deserialize(format, fixture, using=using)
-                                for obj in objects:
-                                    objects_in_fixture += 1
-                                    if router.allow_syncdb(using, obj.object.__class__):
-                                        loaded_objects_in_fixture += 1
-                                        models.add(obj.object.__class__)
-                                        obj.save(using=using)
-                                loaded_object_count += loaded_objects_in_fixture
-                                fixture_object_count += objects_in_fixture
-                                label_found = True
-                            except (SystemExit, KeyboardInterrupt):
-                                raise
-                            except Exception:
-                                import traceback
-                                fixture.close()
-                                if commit:
-                                    transaction.rollback(using=using)
-                                    transaction.leave_transaction_management(using=using)
-                                if show_traceback:
-                                    traceback.print_exc()
-                                else:
-                                    self.stderr.write(
-                                        self.style.ERROR("Problem installing fixture '%s': %s\n" %
-                                             (full_path, ''.join(traceback.format_exception(sys.exc_type,
-                                                 sys.exc_value, sys.exc_traceback)))))
-                                return
-                            fixture.close()
-
-                            # If the fixture we loaded contains 0 objects, assume that an
-                            # error was encountered during fixture loading.
-                            if objects_in_fixture == 0:
-                                self.stderr.write(
-                                    self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" %
-                                        (fixture_name)))
-                                if commit:
-                                    transaction.rollback(using=using)
-                                    transaction.leave_transaction_management(using=using)
-                                return
-
-                    except Exception, e:
-                        if verbosity >= 2:
-                            self.stdout.write("No %s fixture '%s' in %s.\n" % \
-                                (format, fixture_name, humanize(fixture_dir)))
+        try:
+            objs, models, fixture_count, fixture_object_count = find_fixture_data(
+                fixture_labels,
+                verbosity=verbosity,
+                using=using,
+                stdout=self.stdout
+            )
+        except CommandError, e:
+            if commit:
+                transaction.rollback(using=using)
+                transaction.leave_transaction_management(using=using)
+            # For reasons I don't understand CommandErrors are never raised.
+            self.stderr.write(e.args[0] + "\n")
+            return
+        for obj in objs:
+            obj.save(using=using)
 
         # If we found even one object in a fixture, we need to reset the
         # database sequences.
-        if loaded_object_count > 0:
+        if objs:
             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
             if sequence_sql:
                 if verbosity >= 2:
@@ -228,12 +222,12 @@ class Command(BaseCommand):
                 self.stdout.write("No fixtures found.\n")
         else:
             if verbosity >= 1:
-                if fixture_object_count == loaded_object_count:
+                if fixture_object_count == len(objs):
                     self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % (
-                        loaded_object_count, fixture_count))
+                        len(objs), fixture_count))
                 else:
                     self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)\n" % (
-                        loaded_object_count, fixture_object_count, fixture_count))
+                        len(objs), fixture_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
diff --git a/django/test/testcases.py b/django/test/testcases.py
index 65664a1..3cd5a31 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -6,6 +6,7 @@ from xml.dom.minidom import parseString, Node
 from django.conf import settings
 from django.core import mail
 from django.core.management import call_command
+from django.core.management.commands.loaddata import find_fixture_data
 from django.core.urlresolvers import clear_url_caches
 from django.db import transaction, connection, connections, DEFAULT_DB_ALIAS
 from django.http import QueryDict
@@ -238,6 +239,33 @@ class TransactionTestCase(unittest.TestCase):
     # Can be overridden in derived classes.
     client_class = Client
 
+    @classmethod
+    def setUpClass(cls):
+        cls._fixture_objs = {}
+        if hasattr(cls, "fixtures"):
+            for db in cls._get_dbs():
+                cls._fixture_objs[db] = find_fixture_data(
+                    cls.fixtures, verbosity=0, using=db, stdout=sys.stdout
+                )[0]
+
+    @classmethod
+    def tearDownClass(cls):
+        # To save memory I suppose.
+        del cls._fixture_objs
+
+    @classmethod
+    def _get_dbs(cls):
+        if getattr(cls, 'multi_db', False):
+            return connections
+        return [DEFAULT_DB_ALIAS]
+
+    def _load_fixture_objs(self, db):
+        for obj in self._fixture_objs.get(db, []):
+            # DeserializedObject.save clears m2m_data, we get it back in there
+            m2m_data = obj.m2m_data
+            obj.save(using=db)
+            obj.m2m_data = m2m_data
+
     def _pre_setup(self):
         """Performs any pre-test setup. This includes:
 
@@ -255,17 +283,9 @@ class TransactionTestCase(unittest.TestCase):
     def _fixture_setup(self):
         # If the test case has a multi_db=True flag, flush all databases.
         # Otherwise, just flush default.
-        if getattr(self, 'multi_db', False):
-            databases = connections
-        else:
-            databases = [DEFAULT_DB_ALIAS]
-        for db in databases:
+        for db in self._get_dbs():
             call_command('flush', verbosity=0, interactive=False, database=db)
-
-            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, 'database': db})
+            self._load_fixture_objs(db)
 
     def _urlconf_setup(self):
         if hasattr(self, 'urls'):
@@ -535,11 +555,7 @@ class TestCase(TransactionTestCase):
 
         # If the test case has a multi_db=True flag, setup all databases.
         # Otherwise, just use default.
-        if getattr(self, 'multi_db', False):
-            databases = connections
-        else:
-            databases = [DEFAULT_DB_ALIAS]
-
+        databases = self._get_dbs()
         for db in databases:
             transaction.enter_transaction_management(using=db)
             transaction.managed(True, using=db)
@@ -549,12 +565,7 @@ class TestCase(TransactionTestCase):
         Site.objects.clear_cache()
 
         for db in databases:
-            if hasattr(self, 'fixtures'):
-                call_command('loaddata', *self.fixtures, **{
-                                                            'verbosity': 0,
-                                                            'commit': False,
-                                                            'database': db
-                                                            })
+            self._load_fixture_objs(db)
 
     def _fixture_teardown(self):
         if not connections_support_transactions():
