diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 8bf89c90f8..0b752b57e2 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -47,7 +47,8 @@ class Command(BaseCommand): # Keep a count of the installed objects and fixtures fixture_count = 0 - object_count = 0 + loaded_object_count = 0 + fixture_object_count = 0 models = set() humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' @@ -114,7 +115,7 @@ class Command(BaseCommand): if verbosity >= 2: self.stdout.write("Loading '%s' fixtures...\n" % fixture_name) else: - sys.stderr.write( + self.stderr.write( self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" % (fixture_name, format))) transaction.rollback(using=using) @@ -157,17 +158,20 @@ class Command(BaseCommand): 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__): - objects_in_fixture += 1 + loaded_objects_in_fixture += 1 models.add(obj.object.__class__) obj.save(using=using) - object_count += objects_in_fixture + loaded_object_count += loaded_objects_in_fixture + fixture_object_count += objects_in_fixture label_found = True except (SystemExit, KeyboardInterrupt): raise @@ -179,7 +183,7 @@ class Command(BaseCommand): if show_traceback: traceback.print_exc() else: - sys.stderr.write( + 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))))) @@ -189,7 +193,7 @@ class Command(BaseCommand): # If the fixture we loaded contains 0 objects, assume that an # error was encountered during fixture loading. if objects_in_fixture == 0: - sys.stderr.write( + self.stderr.write( self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" % (fixture_name))) transaction.rollback(using=using) @@ -203,7 +207,7 @@ class Command(BaseCommand): # If we found even one object in a fixture, we need to reset the # database sequences. - if object_count > 0: + if loaded_object_count > 0: sequence_sql = connection.ops.sequence_reset_sql(self.style, models) if sequence_sql: if verbosity >= 2: @@ -215,12 +219,17 @@ class Command(BaseCommand): transaction.commit(using=using) transaction.leave_transaction_management(using=using) - if object_count == 0: + if fixture_object_count == 0: if verbosity >= 1: self.stdout.write("No fixtures found.\n") else: if verbosity >= 1: - self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % (object_count, fixture_count)) + if fixture_object_count == loaded_object_count: + self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % ( + loaded_object_count, 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)) # 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/tests/regressiontests/multiple_database/fixtures/pets.json b/tests/regressiontests/multiple_database/fixtures/pets.json new file mode 100644 index 0000000000..89756a3e5b --- /dev/null +++ b/tests/regressiontests/multiple_database/fixtures/pets.json @@ -0,0 +1,18 @@ +[ + { + "pk": 1, + "model": "multiple_database.pet", + "fields": { + "name": "Mr Bigglesworth", + "owner": 1 + } + }, + { + "pk": 2, + "model": "multiple_database.pet", + "fields": { + "name": "Spot", + "owner": 2 + } + } +] \ No newline at end of file diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index b55e708944..7f66ea3bcf 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -1570,11 +1570,31 @@ class UserProfileTestCase(TestCase): self.assertEquals(alice.get_profile().flavor, 'chocolate') self.assertEquals(bob.get_profile().flavor, 'crunchy frog') +class AntiPetRouter(object): + # A router that only expresses an opinion on syncdb, + # passing pets to the 'other' database + + def allow_syncdb(self, db, model): + "Make sure the auth app only appears on the 'other' db" + if db == 'other': + return model._meta.object_name == 'Pet' + else: + return model._meta.object_name != 'Pet' + return None class FixtureTestCase(TestCase): multi_db = True fixtures = ['multidb-common', 'multidb'] + def setUp(self): + # Install the anti-pet router + self.old_routers = router.routers + router.routers = [AntiPetRouter()] + + def tearDown(self): + # Restore the 'other' database as an independent database + router.routers = self.old_routers + def test_fixture_loading(self): "Multi-db fixtures are loaded correctly" # Check that "Pro Django" exists on the default database, but not on other database @@ -1612,6 +1632,14 @@ class FixtureTestCase(TestCase): except Book.DoesNotExist: self.fail('"The Definitive Guide to Django" should exist on both databases') + def test_pseudo_empty_fixtures(self): + "A fixture can contain entries, but lead to nothing in the database; this shouldn't raise an error (ref #14068)" + new_io = StringIO() + management.call_command('loaddata', 'pets', stdout=new_io, stderr=new_io) + command_output = new_io.getvalue().strip() + # No objects will actually be loaded + self.assertEqual(command_output, "Installed 0 object(s) (of 2) from 1 fixture(s)") + class PickleQuerySetTestCase(TestCase): multi_db = True