diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index 7f1d612bf0..1c3f8ba26d 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -26,6 +26,7 @@ class Command(BaseCommand): self.unmodified_files = [] self.post_processed_files = [] self.skipped_files = [] + self.deleted_files = [] self.storage = staticfiles_storage self.style = no_style() @@ -168,6 +169,7 @@ class Command(BaseCommand): "unmodified": self.unmodified_files, "post_processed": self.post_processed_files, "skipped": self.skipped_files, + "deleted": self.deleted_files, } def handle(self, **options): @@ -212,14 +214,21 @@ class Command(BaseCommand): collected = self.collect() if self.verbosity >= 1: + deleted_count = len(collected["deleted"]) modified_count = len(collected["modified"]) unmodified_count = len(collected["unmodified"]) post_processed_count = len(collected["post_processed"]) skipped_count = len(collected["skipped"]) return ( - "\n%(modified_count)s %(identifier)s %(action)s" + "\n%(deleted)s%(modified_count)s %(identifier)s %(action)s" "%(destination)s%(unmodified)s%(post_processed)s%(skipped)s." ) % { + "deleted": ( + "%s static file%s deleted, " + % (deleted_count, "" if deleted_count == 1 else "s") + if deleted_count > 0 + else "" + ), "modified_count": modified_count, "identifier": "static file" + ("" if modified_count == 1 else "s"), "action": "symlinked" if self.symlink else "copied", @@ -264,9 +273,11 @@ class Command(BaseCommand): for f in files: fpath = os.path.join(path, f) if self.dry_run: - self.log("Pretending to delete '%s'" % fpath, level=1) + self.log("Pretending to delete '%s'" % fpath, level=2) + self.deleted_files.append(fpath) else: - self.log("Deleting '%s'" % fpath, level=1) + self.log("Deleting '%s'" % fpath, level=2) + self.deleted_files.append(fpath) try: full_path = self.storage.path(fpath) except NotImplementedError: diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index 4afc172da9..6e213f890a 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -474,6 +474,10 @@ Miscellaneous files due to conflicts when ``--verbosity`` is 1. To see warnings for each conflicting destination path, set the ``--verbosity`` flag to 2 or higher. +* The :option:`collectstatic --clear` command now reports only a summary of + deleted files when ``--verbosity`` is 1. To see the details for each file + deleted, set the ``--verbosity`` flag to 2 or higher. + .. _deprecated-features-6.0: Features deprecated in 6.0 diff --git a/tests/staticfiles_tests/test_management.py b/tests/staticfiles_tests/test_management.py index 9555c54093..c1f8aa9b01 100644 --- a/tests/staticfiles_tests/test_management.py +++ b/tests/staticfiles_tests/test_management.py @@ -291,11 +291,13 @@ class TestCollectionClear(CollectionTestCase): Test the ``--clear`` option of the ``collectstatic`` management command. """ + run_collectstatic_in_setUp = False + def run_collectstatic(self, **kwargs): clear_filepath = os.path.join(settings.STATIC_ROOT, "cleared.txt") with open(clear_filepath, "w") as f: f.write("should be cleared") - super().run_collectstatic(clear=True) + super().run_collectstatic(clear=True, **kwargs) def test_cleared_not_found(self): self.assertFileNotFound("cleared.txt") @@ -316,6 +318,39 @@ class TestCollectionClear(CollectionTestCase): self.run_collectstatic() self.assertFileNotFound("cleared.txt") + def test_verbosity_0(self): + for kwargs in [{}, {"dry_run": True}]: + with self.subTest(kwargs=kwargs): + stdout = StringIO() + self.run_collectstatic(verbosity=0, stdout=stdout, **kwargs) + self.assertEqual(stdout.getvalue(), "") + + def test_verbosity_1(self): + for deletion_message, kwargs in [ + ("Deleting", {}), + ("Pretending to delete", {"dry_run": True}), + ]: + with self.subTest(kwargs=kwargs): + stdout = StringIO() + self.run_collectstatic(verbosity=1, stdout=stdout, **kwargs) + output = stdout.getvalue() + self.assertIn("static file", output) + self.assertIn("deleted", output) + self.assertNotIn(deletion_message, output) + + def test_verbosity_2(self): + for deletion_message, kwargs in [ + ("Deleting", {}), + ("Pretending to delete", {"dry_run": True}), + ]: + with self.subTest(kwargs=kwargs): + stdout = StringIO() + self.run_collectstatic(verbosity=2, stdout=stdout, **kwargs) + output = stdout.getvalue() + self.assertIn("static file", output) + self.assertIn("deleted", output) + self.assertIn(deletion_message, output) + class TestInteractiveMessages(CollectionTestCase): overwrite_warning_msg = "This will overwrite existing files!"