diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 7636239697..7849abbdbb 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -16,10 +16,12 @@ from pathlib import Path import django from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured -from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning from django.utils.functional import LazyObject, empty ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" +DEFAULT_STORAGE_ALIAS = "default" +STATICFILES_STORAGE_ALIAS = "staticfiles" # RemovedInDjango50Warning USE_DEPRECATED_PYTZ_DEPRECATED_MSG = ( @@ -39,6 +41,14 @@ CSRF_COOKIE_MASKED_DEPRECATED_MSG = ( "it will be removed in Django 5.0." ) +DEFAULT_FILE_STORAGE_DEPRECATED_MSG = ( + "The DEFAULT_FILE_STORAGE setting is deprecated. Use STORAGES instead." +) + +STATICFILES_STORAGE_DEPRECATED_MSG = ( + "The STATICFILES_STORAGE setting is deprecated. Use STORAGES instead." +) + class SettingsReference(str): """ @@ -177,6 +187,22 @@ class LazySettings(LazyObject): # paths. return self.__getattr__("USE_L10N") + # RemovedInDjango51Warning. + @property + def DEFAULT_FILE_STORAGE(self): + self._show_deprecation_warning( + DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning + ) + return self.__getattr__("DEFAULT_FILE_STORAGE") + + # RemovedInDjango51Warning. + @property + def STATICFILES_STORAGE(self): + self._show_deprecation_warning( + STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning + ) + return self.__getattr__("STATICFILES_STORAGE") + class Settings: def __init__(self, settings_module): @@ -240,6 +266,20 @@ class Settings: if self.is_overridden("USE_L10N"): warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning) + if self.is_overridden("DEFAULT_FILE_STORAGE"): + if self.is_overridden("STORAGES"): + raise ImproperlyConfigured( + "DEFAULT_FILE_STORAGE/STORAGES are mutually exclusive." + ) + warnings.warn(DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning) + + if self.is_overridden("STATICFILES_STORAGE"): + if self.is_overridden("STORAGES"): + raise ImproperlyConfigured( + "STATICFILES_STORAGE/STORAGES are mutually exclusive." + ) + warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning) + def is_overridden(self, setting): return setting in self._explicit_settings @@ -276,9 +316,29 @@ class UserSettingsHolder: warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning) if name == "CSRF_COOKIE_MASKED": warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning) + if name == "DEFAULT_FILE_STORAGE": + self.STORAGES[DEFAULT_STORAGE_ALIAS] = { + "BACKEND": self.DEFAULT_FILE_STORAGE + } + warnings.warn(DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning) + if name == "STATICFILES_STORAGE": + self.STORAGES[STATICFILES_STORAGE_ALIAS] = { + "BACKEND": self.STATICFILES_STORAGE + } + warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning) super().__setattr__(name, value) if name == "USE_DEPRECATED_PYTZ": warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning) + # RemovedInDjango51Warning. + if name == "STORAGES": + self.STORAGES.setdefault( + DEFAULT_STORAGE_ALIAS, + {"BACKEND": "django.core.files.storage.FileSystemStorage"}, + ) + self.STORAGES.setdefault( + STATICFILES_STORAGE_ALIAS, + {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"}, + ) def __delattr__(self, name): self._deleted.add(name) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index d0ec97e525..7ca9d6c458 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -280,7 +280,14 @@ SECRET_KEY_FALLBACKS = [] # Default file storage mechanism that holds media. DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" -STORAGES = {} +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, +} # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/var/www/example.com/media/" diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index c09f01e446..445cf6b954 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -4,11 +4,11 @@ import posixpath import re from urllib.parse import unquote, urldefrag, urlsplit, urlunsplit -from django.conf import settings +from django.conf import STATICFILES_STORAGE_ALIAS, settings from django.contrib.staticfiles.utils import check_settings, matches_patterns 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.core.files.storage import FileSystemStorage, storages from django.utils.crypto import md5 from django.utils.functional import LazyObject @@ -526,7 +526,7 @@ class ManifestStaticFilesStorage(ManifestFilesMixin, StaticFilesStorage): class ConfiguredStorage(LazyObject): def _setup(self): - self._wrapped = get_storage_class(settings.STATICFILES_STORAGE)() + self._wrapped = storages[STATICFILES_STORAGE_ALIAS] staticfiles_storage = ConfiguredStorage() diff --git a/django/core/files/storage/__init__.py b/django/core/files/storage/__init__.py index ebc887336a..267f71ba37 100644 --- a/django/core/files/storage/__init__.py +++ b/django/core/files/storage/__init__.py @@ -1,4 +1,7 @@ -from django.conf import settings +import warnings + +from django.conf import DEFAULT_STORAGE_ALIAS, settings +from django.utils.deprecation import RemovedInDjango51Warning from django.utils.functional import LazyObject from django.utils.module_loading import import_string @@ -19,14 +22,20 @@ __all__ = ( "storages", ) +GET_STORAGE_CLASS_DEPRECATED_MSG = ( + "django.core.files.storage.get_storage_class is deprecated in favor of " + "using django.core.files.storage.storages." +) + def get_storage_class(import_path=None): + warnings.warn(GET_STORAGE_CLASS_DEPRECATED_MSG, RemovedInDjango51Warning) return import_string(import_path or settings.DEFAULT_FILE_STORAGE) class DefaultStorage(LazyObject): def _setup(self): - self._wrapped = get_storage_class()() + self._wrapped = storages[DEFAULT_STORAGE_ALIAS] storages = StorageHandler() diff --git a/django/core/files/storage/handler.py b/django/core/files/storage/handler.py index ca379c9f5f..ad2054c837 100644 --- a/django/core/files/storage/handler.py +++ b/django/core/files/storage/handler.py @@ -1,4 +1,4 @@ -from django.conf import settings +from django.conf import DEFAULT_STORAGE_ALIAS, STATICFILES_STORAGE_ALIAS, settings from django.core.exceptions import ImproperlyConfigured from django.utils.functional import cached_property from django.utils.module_loading import import_string @@ -19,6 +19,15 @@ class StorageHandler: def backends(self): if self._backends is None: self._backends = settings.STORAGES.copy() + # RemovedInDjango51Warning. + if settings.is_overridden("DEFAULT_FILE_STORAGE"): + self._backends[DEFAULT_STORAGE_ALIAS] = { + "BACKEND": settings.DEFAULT_FILE_STORAGE + } + if settings.is_overridden("STATICFILES_STORAGE"): + self._backends[STATICFILES_STORAGE_ALIAS] = { + "BACKEND": settings.STATICFILES_STORAGE + } return self._backends def __getitem__(self, alias): diff --git a/django/test/signals.py b/django/test/signals.py index 4b270d99fc..94a5161e82 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -13,6 +13,7 @@ from django.dispatch import Signal, receiver from django.utils import timezone from django.utils.formats import FORMAT_SETTINGS, reset_format_cache from django.utils.functional import empty +from django.utils.module_loading import import_string template_rendered = Signal() @@ -113,7 +114,8 @@ def reset_template_engines(*, setting, **kwargs): @receiver(setting_changed) def storages_changed(*, setting, **kwargs): - from django.core.files.storage import storages + from django.contrib.staticfiles.storage import staticfiles_storage + from django.core.files.storage import default_storage, storages if setting in ( "STORAGES", @@ -127,6 +129,9 @@ def storages_changed(*, setting, **kwargs): storages._backends = None storages._storages = {} + default_storage._wrapped = empty + staticfiles_storage._wrapped = empty + @receiver(setting_changed) def clear_serializers_cache(*, setting, **kwargs): @@ -156,11 +161,18 @@ def localize_settings_changed(*, setting, **kwargs): reset_format_cache() +# RemovedInDjango51Warning. @receiver(setting_changed) def file_storage_changed(*, setting, **kwargs): if setting == "DEFAULT_FILE_STORAGE": - from django.core.files.storage import default_storage + from django.conf import DEFAULT_STORAGE_ALIAS + from django.core.files.storage import default_storage, storages + try: + del storages.backends + except AttributeError: + pass + storages._storages[DEFAULT_STORAGE_ALIAS] = import_string(kwargs["value"])() default_storage._wrapped = empty @@ -195,6 +207,17 @@ def static_storage_changed(*, setting, **kwargs): staticfiles_storage._wrapped = empty + # RemovedInDjango51Warning. + if setting == "STATICFILES_STORAGE": + from django.conf import STATICFILES_STORAGE_ALIAS + from django.core.files.storage import storages + + try: + del storages.backends + except AttributeError: + pass + storages._storages[STATICFILES_STORAGE_ALIAS] = import_string(kwargs["value"])() + @receiver(setting_changed) def static_finders_changed(*, setting, **kwargs): diff --git a/docs/howto/static-files/deployment.txt b/docs/howto/static-files/deployment.txt index bd35d13025..67ecf59a71 100644 --- a/docs/howto/static-files/deployment.txt +++ b/docs/howto/static-files/deployment.txt @@ -15,8 +15,8 @@ Serving static files in production The basic outline of putting static files into production consists of two steps: run the :djadmin:`collectstatic` command when static files change, then arrange for the collected static files directory (:setting:`STATIC_ROOT`) to be -moved to the static file server and served. Depending on -:setting:`STATICFILES_STORAGE`, files may need to be moved to a new location +moved to the static file server and served. Depending the ``staticfiles`` +:setting:`STORAGES` alias, files may need to be moved to a new location manually or the :func:`post_process ` method of the ``Storage`` class might take care of that. @@ -85,17 +85,20 @@ There's any number of ways you might do this, but if the provider has an API, you can use a :doc:`custom file storage backend ` to integrate the CDN with your Django project. If you've written or are using a 3rd party custom storage backend, you can tell :djadmin:`collectstatic` to use -it by setting :setting:`STATICFILES_STORAGE` to the storage engine. +it by setting ``staticfiles`` in :setting:`STORAGES`. For example, if you've written an S3 storage backend in ``myproject.storage.S3Storage`` you could use it with:: - STATICFILES_STORAGE = 'myproject.storage.S3Storage' + STORAGES = { + # ... + "staticfiles": {"BACKEND": "myproject.storage.S3Storage"} + } Once that's done, all you have to do is run :djadmin:`collectstatic` and your static files would be pushed through your storage package up to S3. If you later needed to switch to a different storage provider, you may only have to -change your :setting:`STATICFILES_STORAGE` setting. +change ``staticfiles`` in the :setting:`STORAGES` setting. For details on how you'd write one of these backends, see :doc:`/howto/custom-file-storage`. There are 3rd party apps available that @@ -103,6 +106,10 @@ provide storage backends for many common file storage APIs. A good starting point is the `overview at djangopackages.org `_. +.. versionchanged:: 4.2 + + The :setting:`STORAGES` setting was added. + Learn more ========== diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt index 20d21eb07c..def58c5ed1 100644 --- a/docs/howto/static-files/index.txt +++ b/docs/howto/static-files/index.txt @@ -19,7 +19,8 @@ Configuring static files STATIC_URL = 'static/' #. In your templates, use the :ttag:`static` template tag to build the URL for - the given relative path using the configured :setting:`STATICFILES_STORAGE`. + the given relative path using the configured ``staticfiles`` + :setting:`STORAGES` alias. .. _staticfiles-in-templates: diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 1908549e8c..86326c3b4f 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -45,6 +45,12 @@ details on these changes. * Support for passing positional arguments to ``Signer`` and ``TimestampSigner`` will be removed. +* The ``DEFAULT_FILE_STORAGE`` and ``STATICFILES_STORAGE`` settings will be + removed. + +* The ``django.core.files.storage.get_storage_class()`` function will be + removed. + .. _deprecation-removed-in-5.0: 5.0 diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index 08fc23bdb1..a72e2ae286 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -60,11 +60,12 @@ specified by the :setting:`INSTALLED_APPS` setting. The :djadmin:`collectstatic` management command calls the :meth:`~django.contrib.staticfiles.storage.StaticFilesStorage.post_process` -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.ManifestStaticFilesStorage` -by default. +method of the ``staticfiles`` storage backend from :setting:`STORAGES` 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.ManifestStaticFilesStorage` by +default. By default, collected files receive permissions from :setting:`FILE_UPLOAD_PERMISSIONS` and collected directories receive permissions @@ -82,7 +83,7 @@ respectively. For example:: kwargs['directory_permissions_mode'] = 0o760 super().__init__(*args, **kwargs) -Then set the :setting:`STATICFILES_STORAGE` setting to +Then set the ``staticfiles`` storage backend in :setting:`STORAGES` setting to ``'path.to.MyStaticFilesStorage'``. Some commonly used options are: @@ -113,7 +114,8 @@ Some commonly used options are: Don't call the :meth:`~django.contrib.staticfiles.storage.StaticFilesStorage.post_process` - method of the configured :setting:`STATICFILES_STORAGE` storage backend. + method of the configured ``staticfiles`` storage backend from + :setting:`STORAGES`. .. django-admin-option:: --no-default-ignore @@ -360,7 +362,7 @@ attribute. It defaults to 5. To enable the ``ManifestStaticFilesStorage`` you have to make sure the following requirements are met: -* the :setting:`STATICFILES_STORAGE` setting is set to +* the ``staticfiles`` storage backend in :setting:`STORAGES` setting is set to ``'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'`` * the :setting:`DEBUG` setting is set to ``False`` * you've collected all your static files by using the @@ -381,9 +383,9 @@ If a file isn't found in the ``staticfiles.json`` manifest at runtime, a Due to the requirement of running :djadmin:`collectstatic`, this storage typically shouldn't be used when running tests as ``collectstatic`` isn't run -as part of the normal test setup. During testing, ensure that the -:setting:`STATICFILES_STORAGE` setting is set to something else like -``'django.contrib.staticfiles.storage.StaticFilesStorage'`` (the default). +as part of the normal test setup. During testing, ensure that ``staticfiles`` +storage backend in the :setting:`STORAGES` setting is set to something else +like ``'django.contrib.staticfiles.storage.StaticFilesStorage'`` (the default). .. method:: storage.ManifestStaticFilesStorage.file_hash(name, content=None) @@ -434,7 +436,8 @@ files: - The builtin template tag :ttag:`static` which takes a path and urljoins it with the static prefix :setting:`STATIC_URL`. If ``django.contrib.staticfiles`` is installed, the tag uses the ``url()`` - method of the :setting:`STATICFILES_STORAGE` instead. + method of the ``staticfiles`` storage backend from :setting:`STORAGES` + instead. - The builtin template tag :ttag:`get_static_prefix` which populates a template variable with the static prefix :setting:`STATIC_URL` to be @@ -443,6 +446,9 @@ files: - The similar template tag :ttag:`get_media_prefix` which works like :ttag:`get_static_prefix` but uses :setting:`MEDIA_URL`. +- The ``staticfiles`` key in :data:`django.core.files.storage.storages` + contains a ready-to-use instance of the staticfiles storage backend. + .. _staticfiles-development-view: Static file development view diff --git a/docs/ref/files/storage.txt b/docs/ref/files/storage.txt index d5daccf834..47a5788574 100644 --- a/docs/ref/files/storage.txt +++ b/docs/ref/files/storage.txt @@ -18,9 +18,9 @@ Django provides convenient ways to access the default storage class: .. class:: DefaultStorage :class:`~django.core.files.storage.DefaultStorage` provides - lazy access to the current default storage system as defined by - :setting:`DEFAULT_FILE_STORAGE`. :class:`DefaultStorage` uses - :func:`~django.core.files.storage.get_storage_class` internally. + lazy access to the default storage system as defined by ``default`` key in + :setting:`STORAGES`. :class:`DefaultStorage` uses + :data:`~django.core.files.storage.storages` internally. .. data:: default_storage @@ -32,11 +32,16 @@ Django provides convenient ways to access the default storage class: Returns a class or module which implements the storage API. When called without the ``import_path`` parameter ``get_storage_class`` - will return the current default storage system as defined by - :setting:`DEFAULT_FILE_STORAGE`. If ``import_path`` is provided, - ``get_storage_class`` will attempt to import the class or module from the - given path and will return it if successful. An exception will be - raised if the import is unsuccessful. + will return the default storage system as defined by ``default`` key in + :setting:`STORAGES`. If ``import_path`` is provided, ``get_storage_class`` + will attempt to import the class or module from the given path and will + return it if successful. An exception will be raised if the import is + unsuccessful. + + .. deprecated:: 4.2 + + The ``get_storage_class()`` function is deprecated. Use + :data:`storages` instead The ``FileSystemStorage`` class =============================== diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index d1b638f4b6..aa8606a08b 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1356,6 +1356,12 @@ Default: ``'``:class:`django.core.files.storage.FileSystemStorage`\ ``'`` Default file storage class to be used for any file-related operations that don't specify a particular storage system. See :doc:`/topics/files`. +.. deprecated:: 4.2 + + This setting is deprecated. Starting with Django 4.2, default file storage + engine can be configured with the :setting:`STORAGES` setting under the + ``default`` key. + .. setting:: DEFAULT_FROM_EMAIL ``DEFAULT_FROM_EMAIL`` @@ -2615,13 +2621,28 @@ See also the :doc:`/ref/checks` documentation. Default:: - {} + { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + } A dictionary containing the settings for all storages to be used with Django. It is a nested dictionary whose contents map a storage alias to a dictionary containing the options for an individual storage. -Storages can have any alias you choose. +Storages can have any alias you choose. However, there are two aliases with +special significance: + +* ``default`` for :doc:`managing files `. + ``'``:class:`django.core.files.storage.FileSystemStorage`\ ``'`` is the + default storage engine. +* ``staticfiles`` for :doc:`managing static files `. + ``'``:class:`django.contrib.staticfiles.storage.StaticFilesStorage`\ ``'`` is + the default storage engine. The following is an example ``settings.py`` snippet defining a custom file storage called ``example``:: @@ -3598,10 +3619,16 @@ The file storage engine to use when collecting static files with the :djadmin:`collectstatic` management command. A ready-to-use instance of the storage backend defined in this setting -can be found at ``django.contrib.staticfiles.storage.staticfiles_storage``. +can be found under ``staticfiles`` key in ``django.core.files.storage.storages``. For an example, see :ref:`staticfiles-from-cdn`. +.. deprecated:: 4.2 + + This setting is deprecated. Starting with Django 4.2, static files storage + engine can be configured with the :setting:`STORAGES` setting under the + ``staticfiles`` key. + .. setting:: STATICFILES_FINDERS ``STATICFILES_FINDERS`` @@ -3627,8 +3654,8 @@ used. One finder is disabled by default: ``django.contrib.staticfiles.finders.DefaultStorageFinder``. If added to your :setting:`STATICFILES_FINDERS` setting, it will look for static files in -the default file storage as defined by the :setting:`DEFAULT_FILE_STORAGE` -setting. +the default file storage as defined by the ``default`` key in the +:setting:`STORAGES` setting. .. note:: diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 13a8694ad6..5804f455bd 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -2629,7 +2629,7 @@ A set of Django template filters useful for adding a "human touch" to data. See To link to static files that are saved in :setting:`STATIC_ROOT` Django ships with a :ttag:`static` template tag. If the :mod:`django.contrib.staticfiles` app is installed, the tag will serve files using ``url()`` method of the -storage specified by :setting:`STATICFILES_STORAGE`. For example:: +storage specified by ``staticfiles`` in :setting:`STORAGES`. For example:: {% load static %} Hi! diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt index ecc9a06a87..c3b939bf8b 100644 --- a/docs/releases/4.2.txt +++ b/docs/releases/4.2.txt @@ -95,7 +95,12 @@ Custom file storages -------------------- The new :setting:`STORAGES` setting allows configuring multiple custom file -storage backends. +storage backends. It also controls storage engines for managing +:doc:`files ` (the ``"defaut"`` key) and :doc:`static files +` (the ``"staticfiles"`` key). + +The old ``DEFAULT_FILE_STORAGE`` and ``STATICFILES_STORAGE`` settings are +deprecated as of this release. Minor features -------------- @@ -674,3 +679,11 @@ Miscellaneous * Passing positional arguments to ``Signer`` and ``TimestampSigner`` is deprecated in favor of keyword-only arguments. + +* The ``DEFAULT_FILE_STORAGE`` setting is deprecated in favor of + ``STORAGES["default"]``. + +* The ``STATICFILES_STORAGE`` setting is deprecated in favor of + ``STORAGES["staticfiles"]``. + +* The ``django.core.files.storage.get_storage_class()`` function is deprecated. diff --git a/docs/topics/files.txt b/docs/topics/files.txt index eb4e655cfa..ad0dcffdd1 100644 --- a/docs/topics/files.txt +++ b/docs/topics/files.txt @@ -156,9 +156,10 @@ Behind the scenes, Django delegates decisions about how and where to store files to a file storage system. This is the object that actually understands things like file systems, opening and reading files, etc. -Django's default file storage is given by the :setting:`DEFAULT_FILE_STORAGE` -setting; if you don't explicitly provide a storage system, this is the one that -will be used. +Django's default file storage is +``'``:class:`django.core.files.storage.FileSystemStorage`\ ``'``. If you don't +explicitly provide a storage system in the ``default`` key of the +:setting:`STORAGES` setting, this is the one that will be used. See below for details of the built-in default file storage system, and see :doc:`/howto/custom-file-storage` for information on writing your own file diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 524fc85584..1ecc97a2a9 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1441,16 +1441,15 @@ when settings are changed. Django itself uses this signal to reset various data: -================================= ======================== -Overridden settings Data reset -================================= ======================== -USE_TZ, TIME_ZONE Databases timezone -TEMPLATES Template engines -SERIALIZATION_MODULES Serializers cache -LOCALE_PATHS, LANGUAGE_CODE Default translation and loaded translations -MEDIA_ROOT, DEFAULT_FILE_STORAGE Default file storage -STATIC_ROOT, STATIC_URL, STORAGES Storages configuration -================================= ======================== +============================================================================ ======================== +Overridden settings Data reset +============================================================================ ======================== +USE_TZ, TIME_ZONE Databases timezone +TEMPLATES Template engines +SERIALIZATION_MODULES Serializers cache +LOCALE_PATHS, LANGUAGE_CODE Default translation and loaded translations +DEFAULT_FILE_STORAGE, STATICFILES_STORAGE, STATIC_ROOT, STATIC_URL, STORAGES Storages configuration +============================================================================ ======================== Isolating apps -------------- diff --git a/tests/deprecation/test_storages.py b/tests/deprecation/test_storages.py new file mode 100644 index 0000000000..71ed3acdb0 --- /dev/null +++ b/tests/deprecation/test_storages.py @@ -0,0 +1,165 @@ +import sys +from types import ModuleType + +from django.conf import ( + DEFAULT_FILE_STORAGE_DEPRECATED_MSG, + DEFAULT_STORAGE_ALIAS, + STATICFILES_STORAGE_ALIAS, + STATICFILES_STORAGE_DEPRECATED_MSG, + Settings, + settings, +) +from django.contrib.staticfiles.storage import ( + ManifestStaticFilesStorage, + staticfiles_storage, +) +from django.core.exceptions import ImproperlyConfigured +from django.core.files.storage import Storage, StorageHandler, default_storage, storages +from django.test import TestCase, ignore_warnings +from django.utils.deprecation import RemovedInDjango51Warning + + +class StaticfilesStorageDeprecationTests(TestCase): + msg = STATICFILES_STORAGE_DEPRECATED_MSG + + def test_override_settings_warning(self): + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + with self.settings( + STATICFILES_STORAGE=( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + ): + pass + + def test_settings_init(self): + settings_module = ModuleType("fake_settings_module") + settings_module.USE_TZ = True + settings_module.STATICFILES_STORAGE = ( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + sys.modules["fake_settings_module"] = settings_module + try: + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + Settings("fake_settings_module") + finally: + del sys.modules["fake_settings_module"] + + def test_access_warning(self): + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + settings.STATICFILES_STORAGE + # Works a second time. + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + settings.STATICFILES_STORAGE + + @ignore_warnings(category=RemovedInDjango51Warning) + def test_access(self): + with self.settings( + STATICFILES_STORAGE=( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + ): + self.assertEqual( + settings.STATICFILES_STORAGE, + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + ) + # Works a second time. + self.assertEqual( + settings.STATICFILES_STORAGE, + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + ) + + def test_use_both_error(self): + msg = "STATICFILES_STORAGE/STORAGES are mutually exclusive." + settings_module = ModuleType("fake_settings_module") + settings_module.USE_TZ = True + settings_module.STATICFILES_STORAGE = ( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + settings_module.STORAGES = {} + sys.modules["fake_settings_module"] = settings_module + try: + with self.assertRaisesMessage(ImproperlyConfigured, msg): + Settings("fake_settings_module") + finally: + del sys.modules["fake_settings_module"] + + @ignore_warnings(category=RemovedInDjango51Warning) + def test_storage(self): + empty_storages = StorageHandler() + with self.settings( + STATICFILES_STORAGE=( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + ): + self.assertIsInstance( + storages[STATICFILES_STORAGE_ALIAS], + ManifestStaticFilesStorage, + ) + self.assertIsInstance( + empty_storages[STATICFILES_STORAGE_ALIAS], + ManifestStaticFilesStorage, + ) + self.assertIsInstance(staticfiles_storage, ManifestStaticFilesStorage) + + +class DefaultStorageDeprecationTests(TestCase): + msg = DEFAULT_FILE_STORAGE_DEPRECATED_MSG + + def test_override_settings_warning(self): + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + with self.settings( + DEFAULT_FILE_STORAGE=("django.core.files.storage.Storage") + ): + pass + + def test_settings_init(self): + settings_module = ModuleType("fake_settings_module") + settings_module.USE_TZ = True + settings_module.DEFAULT_FILE_STORAGE = "django.core.files.storage.Storage" + sys.modules["fake_settings_module"] = settings_module + try: + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + Settings("fake_settings_module") + finally: + del sys.modules["fake_settings_module"] + + def test_access_warning(self): + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + settings.DEFAULT_FILE_STORAGE + # Works a second time. + with self.assertRaisesMessage(RemovedInDjango51Warning, self.msg): + settings.DEFAULT_FILE_STORAGE + + @ignore_warnings(category=RemovedInDjango51Warning) + def test_access(self): + with self.settings(DEFAULT_FILE_STORAGE="django.core.files.storage.Storage"): + self.assertEqual( + settings.DEFAULT_FILE_STORAGE, + "django.core.files.storage.Storage", + ) + # Works a second time. + self.assertEqual( + settings.DEFAULT_FILE_STORAGE, + "django.core.files.storage.Storage", + ) + + def test_use_both_error(self): + msg = "DEFAULT_FILE_STORAGE/STORAGES are mutually exclusive." + settings_module = ModuleType("fake_settings_module") + settings_module.USE_TZ = True + settings_module.DEFAULT_FILE_STORAGE = "django.core.files.storage.Storage" + settings_module.STORAGES = {} + sys.modules["fake_settings_module"] = settings_module + try: + with self.assertRaisesMessage(ImproperlyConfigured, msg): + Settings("fake_settings_module") + finally: + del sys.modules["fake_settings_module"] + + @ignore_warnings(category=RemovedInDjango51Warning) + def test_storage(self): + empty_storages = StorageHandler() + with self.settings(DEFAULT_FILE_STORAGE="django.core.files.storage.Storage"): + self.assertIsInstance(storages[DEFAULT_STORAGE_ALIAS], Storage) + self.assertIsInstance(empty_storages[DEFAULT_STORAGE_ALIAS], Storage) + self.assertIsInstance(default_storage, Storage) diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index d4e5969519..4616aad10a 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -11,10 +11,15 @@ from io import StringIO from pathlib import Path from urllib.request import urlopen +from django.conf import DEFAULT_STORAGE_ALIAS, STATICFILES_STORAGE_ALIAS from django.core.cache import cache from django.core.exceptions import SuspiciousFileOperation from django.core.files.base import ContentFile, File -from django.core.files.storage import FileSystemStorage, InvalidStorageError +from django.core.files.storage import ( + GET_STORAGE_CLASS_DEPRECATED_MSG, + FileSystemStorage, + InvalidStorageError, +) from django.core.files.storage import Storage as BaseStorage from django.core.files.storage import ( StorageHandler, @@ -30,10 +35,11 @@ from django.core.files.uploadedfile import ( from django.db.models import FileField from django.db.models.fields.files import FileDescriptor from django.test import LiveServerTestCase, SimpleTestCase, TestCase, override_settings -from django.test.utils import requires_tz_support +from django.test.utils import ignore_warnings, requires_tz_support from django.urls import NoReverseMatch, reverse_lazy from django.utils import timezone from django.utils._os import symlinks_supported +from django.utils.deprecation import RemovedInDjango51Warning from .models import Storage, callable_storage, temp_storage, temp_storage_location @@ -41,6 +47,7 @@ FILE_SUFFIX_REGEX = "[A-Za-z0-9]{7}" class GetStorageClassTests(SimpleTestCase): + @ignore_warnings(category=RemovedInDjango51Warning) def test_get_filesystem_storage(self): """ get_storage_class returns the class for a storage backend name/path. @@ -50,6 +57,7 @@ class GetStorageClassTests(SimpleTestCase): FileSystemStorage, ) + @ignore_warnings(category=RemovedInDjango51Warning) def test_get_invalid_storage_module(self): """ get_storage_class raises an error if the requested import don't exist. @@ -57,6 +65,7 @@ class GetStorageClassTests(SimpleTestCase): with self.assertRaisesMessage(ImportError, "No module named 'storage'"): get_storage_class("storage.NonexistentStorage") + @ignore_warnings(category=RemovedInDjango51Warning) def test_get_nonexistent_storage_class(self): """ get_storage_class raises an error if the requested class don't exist. @@ -64,6 +73,7 @@ class GetStorageClassTests(SimpleTestCase): with self.assertRaises(ImportError): get_storage_class("django.core.files.storage.NonexistentStorage") + @ignore_warnings(category=RemovedInDjango51Warning) def test_get_nonexistent_storage_module(self): """ get_storage_class raises an error if the requested module don't exist. @@ -75,6 +85,11 @@ class GetStorageClassTests(SimpleTestCase): "django.core.files.nonexistent_storage.NonexistentStorage" ) + def test_deprecation_warning(self): + msg = GET_STORAGE_CLASS_DEPRECATED_MSG + with self.assertRaisesMessage(RemovedInDjango51Warning, msg): + get_storage_class("django.core.files.storage.FileSystemStorage"), + class FileSystemStorageTests(unittest.TestCase): def test_deconstruction(self): @@ -1179,7 +1194,17 @@ class StorageHandlerTests(SimpleTestCase): def test_defaults(self): storages = StorageHandler() - self.assertEqual(storages.backends, {}) + self.assertEqual( + storages.backends, + { + DEFAULT_STORAGE_ALIAS: { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + }, + ) def test_nonexistent_alias(self): msg = "Could not find config for 'nonexistent' in settings.STORAGES." diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index f3d926ac5f..693efc4c62 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -9,6 +9,7 @@ from io import BytesIO, StringIO from unittest import mock from urllib.parse import quote +from django.conf import DEFAULT_STORAGE_ALIAS from django.core.exceptions import SuspiciousFileOperation from django.core.files import temp as tempfile from django.core.files.storage import default_storage @@ -806,7 +807,11 @@ class DirectoryCreationTests(SimpleTestCase): sys.platform == "win32", "Python on Windows doesn't have working os.chmod()." ) @override_settings( - DEFAULT_FILE_STORAGE="django.core.files.storage.FileSystemStorage" + STORAGES={ + DEFAULT_STORAGE_ALIAS: { + "BACKEND": "django.core.files.storage.FileSystemStorage", + } + } ) def test_readonly_root(self): """Permission errors are not swallowed""" diff --git a/tests/staticfiles_tests/test_forms.py b/tests/staticfiles_tests/test_forms.py index ad662d7321..489140a62c 100644 --- a/tests/staticfiles_tests/test_forms.py +++ b/tests/staticfiles_tests/test_forms.py @@ -1,5 +1,6 @@ from urllib.parse import urljoin +from django.conf import STATICFILES_STORAGE_ALIAS from django.contrib.staticfiles import storage from django.forms import Media from django.templatetags.static import static @@ -12,9 +13,13 @@ class StaticTestStorage(storage.StaticFilesStorage): @override_settings( - STATIC_URL="http://media.example.com/static/", INSTALLED_APPS=("django.contrib.staticfiles",), - STATICFILES_STORAGE="staticfiles_tests.test_forms.StaticTestStorage", + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.test_forms.StaticTestStorage", + "OPTIONS": {"location": "http://media.example.com/static/"}, + } + }, ) class StaticFilesFormsMediaTestCase(SimpleTestCase): def test_absolute_url(self): diff --git a/tests/staticfiles_tests/test_management.py b/tests/staticfiles_tests/test_management.py index 6dec3a67bd..8398195cec 100644 --- a/tests/staticfiles_tests/test_management.py +++ b/tests/staticfiles_tests/test_management.py @@ -9,7 +9,7 @@ from unittest import mock from admin_scripts.tests import AdminScriptTestCase -from django.conf import settings +from django.conf import STATICFILES_STORAGE_ALIAS, settings from django.contrib.staticfiles import storage from django.contrib.staticfiles.management.commands import collectstatic, runserver from django.core.exceptions import ImproperlyConfigured @@ -141,16 +141,24 @@ class TestConfiguration(StaticFilesTestCase): try: storage.staticfiles_storage._wrapped = empty with self.settings( - STATICFILES_STORAGE=( - "django.contrib.staticfiles.storage.StaticFilesStorage" - ) + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": ( + "django.contrib.staticfiles.storage.StaticFilesStorage" + ) + } + } ): command = collectstatic.Command() self.assertTrue(command.is_local_storage()) storage.staticfiles_storage._wrapped = empty with self.settings( - STATICFILES_STORAGE="staticfiles_tests.storage.DummyStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.DummyStorage" + } + } ): command = collectstatic.Command() self.assertFalse(command.is_local_storage()) @@ -241,9 +249,13 @@ class TestCollectionVerbosity(CollectionTestCase): self.assertIn(self.copying_msg, output) @override_settings( - STATICFILES_STORAGE=( - "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" - ) + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": ( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + }, + } ) def test_verbosity_1_with_post_process(self): stdout = StringIO() @@ -251,9 +263,13 @@ class TestCollectionVerbosity(CollectionTestCase): self.assertNotIn(self.post_process_msg, stdout.getvalue()) @override_settings( - STATICFILES_STORAGE=( - "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" - ) + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": ( + "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + ) + }, + } ) def test_verbosity_2_with_post_process(self): stdout = StringIO() @@ -280,7 +296,11 @@ class TestCollectionClear(CollectionTestCase): super().run_collectstatic(clear=True) @override_settings( - STATICFILES_STORAGE="staticfiles_tests.storage.PathNotImplementedStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.PathNotImplementedStorage" + }, + } ) def test_handle_path_notimplemented(self): self.run_collectstatic() @@ -395,7 +415,11 @@ class TestCollectionDryRun(TestNoFilesCreated, CollectionTestCase): @override_settings( - STATICFILES_STORAGE="django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" + }, + } ) class TestCollectionDryRunManifestStaticFilesStorage(TestCollectionDryRun): pass @@ -518,7 +542,13 @@ class TestCollectionOverwriteWarning(CollectionTestCase): self.assertNotIn(self.warning_string, output) -@override_settings(STATICFILES_STORAGE="staticfiles_tests.storage.DummyStorage") +@override_settings( + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.DummyStorage" + }, + } +) class TestCollectionNonLocalStorage(TestNoFilesCreated, CollectionTestCase): """ Tests for a Storage that implements get_modified_time() but not path() @@ -540,7 +570,11 @@ class TestCollectionNonLocalStorage(TestNoFilesCreated, CollectionTestCase): class TestCollectionNeverCopyStorage(CollectionTestCase): @override_settings( - STATICFILES_STORAGE="staticfiles_tests.storage.NeverCopyRemoteStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.NeverCopyRemoteStorage" + }, + } ) def test_skips_newer_files_in_remote_storage(self): """ @@ -607,7 +641,11 @@ class TestCollectionLinks(TestDefaults, CollectionTestCase): self.assertFalse(os.path.lexists(broken_symlink_path)) @override_settings( - STATICFILES_STORAGE="staticfiles_tests.storage.PathNotImplementedStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.PathNotImplementedStorage" + } + } ) def test_no_remote_link(self): with self.assertRaisesMessage( diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index f2f1899aac..11b160945e 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -8,7 +8,7 @@ from io import StringIO from pathlib import Path from unittest import mock -from django.conf import settings +from django.conf import STATICFILES_STORAGE_ALIAS, settings from django.contrib.staticfiles import finders, storage from django.contrib.staticfiles.management.commands.collectstatic import ( Command as CollectstaticCommand, @@ -369,7 +369,13 @@ class TestHashedFiles: self.assertPostCondition() -@override_settings(STATICFILES_STORAGE="staticfiles_tests.storage.ExtraPatternsStorage") +@override_settings( + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.ExtraPatternsStorage", + }, + } +) class TestExtraPatternsStorage(CollectionTestCase): def setUp(self): storage.staticfiles_storage.hashed_files.clear() # avoid cache interference @@ -399,7 +405,11 @@ class TestExtraPatternsStorage(CollectionTestCase): @override_settings( - STATICFILES_STORAGE="django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + }, + } ) class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase): """ @@ -559,7 +569,13 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase): self.assertEqual(manifest_content, {"dummy.txt": "dummy.txt"}) -@override_settings(STATICFILES_STORAGE="staticfiles_tests.storage.NoneHashStorage") +@override_settings( + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.NoneHashStorage", + }, + } +) class TestCollectionNoneHashStorage(CollectionTestCase): hashed_file_path = hashed_file_path @@ -569,7 +585,11 @@ class TestCollectionNoneHashStorage(CollectionTestCase): @override_settings( - STATICFILES_STORAGE="staticfiles_tests.storage.NoPostProcessReplacedPathStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.NoPostProcessReplacedPathStorage", + }, + } ) class TestCollectionNoPostProcessReplacedPaths(CollectionTestCase): run_collectstatic_in_setUp = False @@ -580,7 +600,13 @@ class TestCollectionNoPostProcessReplacedPaths(CollectionTestCase): self.assertIn("post-processed", stdout.getvalue()) -@override_settings(STATICFILES_STORAGE="staticfiles_tests.storage.SimpleStorage") +@override_settings( + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.SimpleStorage", + }, + } +) class TestCollectionSimpleStorage(CollectionTestCase): hashed_file_path = hashed_file_path @@ -733,7 +759,11 @@ class TestStaticFilePermissions(CollectionTestCase): @override_settings( FILE_UPLOAD_PERMISSIONS=0o655, FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o765, - STATICFILES_STORAGE="staticfiles_tests.test_storage.CustomStaticFilesStorage", + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.test_storage.CustomStaticFilesStorage", + }, + }, ) def test_collect_static_files_subclass_of_static_storage(self): call_command("collectstatic", **self.command_params) @@ -753,7 +783,11 @@ class TestStaticFilePermissions(CollectionTestCase): @override_settings( - STATICFILES_STORAGE="django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + }, + } ) class TestCollectionHashedFilesCache(CollectionTestCase): """ diff --git a/tests/staticfiles_tests/test_templatetags.py b/tests/staticfiles_tests/test_templatetags.py index 4cc3f95ab1..9b8bcf1938 100644 --- a/tests/staticfiles_tests/test_templatetags.py +++ b/tests/staticfiles_tests/test_templatetags.py @@ -1,3 +1,4 @@ +from django.conf import STATICFILES_STORAGE_ALIAS from django.test import override_settings from .cases import StaticFilesTestCase @@ -12,7 +13,11 @@ class TestTemplateTag(StaticFilesTestCase): ) @override_settings( - STATICFILES_STORAGE="staticfiles_tests.storage.QueryStringStorage" + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "staticfiles_tests.storage.QueryStringStorage" + }, + } ) def test_template_tag_escapes(self): """ diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 79757045dd..a377479d38 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -5,7 +5,7 @@ import warnings from io import StringIO from unittest import mock -from django.conf import settings +from django.conf import STATICFILES_STORAGE_ALIAS, settings from django.contrib.staticfiles.finders import get_finder, get_finders from django.contrib.staticfiles.storage import staticfiles_storage from django.core.exceptions import ImproperlyConfigured @@ -2106,12 +2106,14 @@ class OverrideSettingsTests(SimpleTestCase): def test_override_staticfiles_storage(self): """ - Overriding the STATICFILES_STORAGE setting should be reflected in + Overriding the STORAGES setting should be reflected in the value of django.contrib.staticfiles.storage.staticfiles_storage. """ new_class = "ManifestStaticFilesStorage" new_storage = "django.contrib.staticfiles.storage." + new_class - with self.settings(STATICFILES_STORAGE=new_storage): + with self.settings( + STORAGES={STATICFILES_STORAGE_ALIAS: {"BACKEND": new_storage}} + ): self.assertEqual(staticfiles_storage.__class__.__name__, new_class) def test_override_staticfiles_finders(self):