diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 088963102ec..f6f335ce13b 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -3,6 +3,7 @@ import json import os import posixpath import re +import warnings from collections import OrderedDict from urllib.parse import unquote, urldefrag, urlsplit, urlunsplit @@ -14,6 +15,7 @@ from django.core.cache import ( from django.core.exceptions import ImproperlyConfigured from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage, get_storage_class +from django.utils.deprecation import RemovedInDjango31Warning from django.utils.functional import LazyObject @@ -474,7 +476,13 @@ class CachedStaticFilesStorage(CachedFilesMixin, StaticFilesStorage): A static file system storage backend which also saves hashed copies of the files it saves. """ - pass + def __init__(self, *args, **kwargs): + warnings.warn( + 'CachedStaticFilesStorage is deprecated in favor of ' + 'ManifestStaticFilesStorage.', + RemovedInDjango31Warning, stacklevel=2, + ) + super().__init__(*args, **kwargs) class ManifestStaticFilesStorage(ManifestFilesMixin, StaticFilesStorage): diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 4a40300442d..8c81744e179 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -26,6 +26,9 @@ details on these changes. * The ``FILE_CHARSET`` setting will be removed. +* ``django.contrib.staticfiles.storage.CachedStaticFilesStorage`` will be + removed. + .. _deprecation-removed-in-3.0: 3.0 diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index 3b78d1f8970..c17518d2b44 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -62,7 +62,7 @@ The :djadmin:`collectstatic` management command calls the method of the :setting:`STATICFILES_STORAGE` after each run and passes a list of paths that have been found by the management command. It also receives all command line options of :djadmin:`collectstatic`. This is used -by the :class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` +by the :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` by default. By default, collected files receive permissions from @@ -229,9 +229,7 @@ local development, should **never be used in production** and is only available if the :doc:`staticfiles ` app is in your project's :setting:`INSTALLED_APPS` setting. -``--insecure`` doesn't work with -:class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` or -:class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage`. +``--insecure`` doesn't work with :class:`~.storage.ManifestStaticFilesStorage`. Example usage:: @@ -262,7 +260,7 @@ line options. It yields tuples of three values: ``processed`` is a boolean indicating whether or not the value was post-processed, or an exception if post-processing failed. -The :class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` +The :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` uses this behind the scenes to replace the paths with their hashed counterparts and update the cache appropriately. @@ -362,6 +360,13 @@ hashing algorithm. .. class:: storage.CachedStaticFilesStorage +.. deprecated:: 2.1 + + ``CachedStaticFilesStorage`` is deprecated as it has some intractable + problems, some of which are outlined below. Use + :class:`~storage.ManifestStaticFilesStorage` or a third-party cloud storage + instead. + ``CachedStaticFilesStorage`` is a similar class like the :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` class but uses Django's :doc:`caching framework` for storing the diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 67be54b8560..ea08d4cf3d8 100644 --- a/docs/releases/1.4.txt +++ b/docs/releases/1.4.txt @@ -466,15 +466,12 @@ files from a cloud service`. -------------------------------------------- The :mod:`staticfiles` contrib app now has a -:class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` backend +``django.contrib.staticfiles.storage.CachedStaticFilesStorage`` backend that caches the files it saves (when running the :djadmin:`collectstatic` management command) by appending the MD5 hash of the file's content to the filename. For example, the file ``css/styles.css`` would also be saved as ``css/styles.55e7cbb9ba48.css`` -See the :class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` -docs for more information. - Simple clickjacking protection ------------------------------ diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index ac54380561b..71688eb80f8 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -506,8 +506,7 @@ Minor features and :attr:`~django.core.files.storage.FileSystemStorage.directory_permissions_mode` parameters. See :djadmin:`collectstatic` for example usage. -* The :class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` - backend gets a sibling class called +* The ``CachedStaticFilesStorage`` backend gets a sibling class called :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` that doesn't use the cache system at all but instead a JSON file called ``staticfiles.json`` for storing the mapping between the original file name diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt index 5d76289da69..03b9070a48e 100644 --- a/docs/releases/2.2.txt +++ b/docs/releases/2.2.txt @@ -354,3 +354,7 @@ Miscellaneous * The ``FILE_CHARSET`` setting is deprecated. Starting with Django 3.1, files read from disk must be UTF-8 encoded. + +* ``django.contrib.staticfiles.storage.CachedStaticFilesStorage`` is + deprecated due to the intractable problems that is has. Use + :class:`.ManifestStaticFilesStorage` or a third-party cloud storage instead. diff --git a/docs/topics/performance.txt b/docs/topics/performance.txt index ebc192c0ab3..4ccf158241e 100644 --- a/docs/topics/performance.txt +++ b/docs/topics/performance.txt @@ -290,13 +290,13 @@ Static files Static files, which by definition are not dynamic, make an excellent target for optimization gains. -:class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By taking advantage of web browsers' caching abilities, you can eliminate network hits entirely for a given file after the initial download. -:class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage` appends a +:class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` appends a content-dependent tag to the filenames of :doc:`static files ` to make it safe for browsers to cache them long-term without missing future changes - when a file changes, so will the diff --git a/tests/staticfiles_tests/storage.py b/tests/staticfiles_tests/storage.py index 3214a68a00f..b9cac3cd057 100644 --- a/tests/staticfiles_tests/storage.py +++ b/tests/staticfiles_tests/storage.py @@ -2,7 +2,7 @@ import os from datetime import datetime, timedelta from django.conf import settings -from django.contrib.staticfiles.storage import CachedStaticFilesStorage +from django.contrib.staticfiles.storage import ManifestStaticFilesStorage from django.core.files import storage from django.utils import timezone @@ -70,18 +70,18 @@ class QueryStringStorage(storage.Storage): return path + '?a=b&c=d' -class SimpleCachedStaticFilesStorage(CachedStaticFilesStorage): +class SimpleStorage(ManifestStaticFilesStorage): def file_hash(self, name, content=None): return 'deploy12345' -class ExtraPatternsCachedStaticFilesStorage(CachedStaticFilesStorage): +class ExtraPatternsStorage(ManifestStaticFilesStorage): """ A storage class to test pattern substitutions with more than one pattern entry. The added pattern rewrites strings like "url(...)" to JS_URL("..."). """ - patterns = tuple(CachedStaticFilesStorage.patterns) + ( + patterns = tuple(ManifestStaticFilesStorage.patterns) + ( ( "*.js", ( (r"""(url\(['"]{0,1}\s*(.*?)["']{0,1}\))""", 'JS_URL("%s")'), diff --git a/tests/staticfiles_tests/test_management.py b/tests/staticfiles_tests/test_management.py index 20595ad1185..1fdeaf78cc7 100644 --- a/tests/staticfiles_tests/test_management.py +++ b/tests/staticfiles_tests/test_management.py @@ -201,13 +201,13 @@ class TestCollectionVerbosity(CollectionTestCase): self.assertIn(self.staticfiles_copied_msg, output) self.assertIn(self.copying_msg, output) - @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage') + @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.ManifestStaticFilesStorage') def test_verbosity_1_with_post_process(self): stdout = StringIO() self.run_collectstatic(verbosity=1, stdout=stdout, post_process=True) self.assertNotIn(self.post_process_msg, stdout.getvalue()) - @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage') + @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.ManifestStaticFilesStorage') def test_verbosity_2_with_post_process(self): stdout = StringIO() self.run_collectstatic(verbosity=2, stdout=stdout, post_process=True) diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index f7a22b7427d..97e3b9113d6 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -12,7 +12,8 @@ from django.contrib.staticfiles.management.commands.collectstatic import ( ) from django.core.cache.backends.base import BaseCache from django.core.management import call_command -from django.test import override_settings +from django.test import SimpleTestCase, ignore_warnings, override_settings +from django.utils.deprecation import RemovedInDjango31Warning from .cases import CollectionTestCase from .settings import TEST_ROOT @@ -43,9 +44,6 @@ class TestHashedFiles: pass def test_template_tag_return(self): - """ - Test the CachedStaticFilesStorage backend. - """ self.assertStaticRaises(ValueError, "does/not/exist.png", "/static/does/not/exist.png") self.assertStaticRenders("test/file.txt", "/static/test/file.dad0999e4f8f.txt") self.assertStaticRenders("test/file.txt", "/static/test/file.dad0999e4f8f.txt", asvar=True) @@ -232,6 +230,7 @@ class TestHashedFiles: self.assertPostCondition() +@ignore_warnings(category=RemovedInDjango31Warning) @override_settings( STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage', ) @@ -299,10 +298,20 @@ class TestCollectionCachedStorage(TestHashedFiles, CollectionTestCase): self.hashed_file_path('cached/styles.css') -@override_settings( - STATICFILES_STORAGE='staticfiles_tests.storage.ExtraPatternsCachedStaticFilesStorage', -) -class TestExtraPatternsCachedStorage(CollectionTestCase): +class TestCachedStaticFilesStorageDeprecation(SimpleTestCase): + def test_warning(self): + from django.contrib.staticfiles.storage import CachedStaticFilesStorage + from django.utils.deprecation import RemovedInDjango31Warning + msg = ( + 'CachedStaticFilesStorage is deprecated in favor of ' + 'ManifestStaticFilesStorage.' + ) + with self.assertRaisesMessage(RemovedInDjango31Warning, msg): + CachedStaticFilesStorage() + + +@override_settings(STATICFILES_STORAGE='staticfiles_tests.storage.ExtraPatternsStorage') +class TestExtraPatternsStorage(CollectionTestCase): def setUp(self): storage.staticfiles_storage.hashed_files.clear() # avoid cache interference @@ -437,13 +446,8 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase): self.hashed_file_path(missing_file_name) -@override_settings( - STATICFILES_STORAGE='staticfiles_tests.storage.SimpleCachedStaticFilesStorage', -) -class TestCollectionSimpleCachedStorage(CollectionTestCase): - """ - Tests for the Cache busting storage - """ +@override_settings(STATICFILES_STORAGE='staticfiles_tests.storage.SimpleStorage') +class TestCollectionSimpleStorage(CollectionTestCase): hashed_file_path = hashed_file_path def setUp(self): @@ -451,9 +455,6 @@ class TestCollectionSimpleCachedStorage(CollectionTestCase): super().setUp() def test_template_tag_return(self): - """ - Test the CachedStaticFilesStorage backend. - """ self.assertStaticRaises(ValueError, "does/not/exist.png", "/static/does/not/exist.png") self.assertStaticRenders("test/file.txt", "/static/test/file.deploy12345.txt") self.assertStaticRenders("cached/styles.css", "/static/cached/styles.deploy12345.css") @@ -543,7 +544,7 @@ class TestStaticFilePermissions(CollectionTestCase): @override_settings( - STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage', + STATICFILES_STORAGE='django.contrib.staticfiles.storage.ManifestStaticFilesStorage', ) class TestCollectionHashedFilesCache(CollectionTestCase): """ diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index bfed8c81b2f..b4845186872 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -1092,7 +1092,7 @@ class OverrideSettingsTests(SimpleTestCase): Overriding the STATICFILES_STORAGE setting should be reflected in the value of django.contrib.staticfiles.storage.staticfiles_storage. """ - new_class = 'CachedStaticFilesStorage' + new_class = 'ManifestStaticFilesStorage' new_storage = 'django.contrib.staticfiles.storage.' + new_class with self.settings(STATICFILES_STORAGE=new_storage): self.assertEqual(staticfiles_storage.__class__.__name__, new_class)