diff --git a/django/contrib/staticfiles/apps.py b/django/contrib/staticfiles/apps.py index 67acf042aa..08d827c5cd 100644 --- a/django/contrib/staticfiles/apps.py +++ b/django/contrib/staticfiles/apps.py @@ -1,5 +1,5 @@ from django.apps import AppConfig -from django.contrib.staticfiles.checks import check_finders +from django.contrib.staticfiles.checks import check_finders, check_storages from django.core import checks from django.utils.translation import gettext_lazy as _ @@ -11,3 +11,4 @@ class StaticFilesConfig(AppConfig): def ready(self): checks.register(check_finders, checks.Tags.staticfiles) + checks.register(check_storages, checks.Tags.staticfiles) diff --git a/django/contrib/staticfiles/checks.py b/django/contrib/staticfiles/checks.py index fb57bf726d..06b87c281c 100644 --- a/django/contrib/staticfiles/checks.py +++ b/django/contrib/staticfiles/checks.py @@ -1,4 +1,11 @@ +from django.conf import STATICFILES_STORAGE_ALIAS, settings from django.contrib.staticfiles.finders import get_finders +from django.core.checks import Error + +E005 = Error( + f"The STORAGES setting must define a '{STATICFILES_STORAGE_ALIAS}' storage.", + id="staticfiles.E005", +) def check_finders(app_configs=None, **kwargs): @@ -12,3 +19,11 @@ def check_finders(app_configs=None, **kwargs): else: errors.extend(finder_errors) return errors + + +def check_storages(app_configs=None, **kwargs): + """Ensure staticfiles is defined in STORAGES setting.""" + errors = [] + if STATICFILES_STORAGE_ALIAS not in settings.STORAGES: + errors.append(E005) + return errors diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index cd539aa964..b8789ecf6f 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -911,3 +911,5 @@ configured: :setting:`STATICFILES_DIRS` setting must not end with a slash. * **staticfiles.W004**: The directory ```` in the :setting:`STATICFILES_DIRS` does not exist. +* **staticfiles.E005**: The :setting:`STORAGES` setting must define a + ``staticfiles`` storage. diff --git a/tests/staticfiles_tests/test_checks.py b/tests/staticfiles_tests/test_checks.py index a8c6b78a96..14f72ef357 100644 --- a/tests/staticfiles_tests/test_checks.py +++ b/tests/staticfiles_tests/test_checks.py @@ -1,11 +1,11 @@ from pathlib import Path from unittest import mock -from django.conf import settings -from django.contrib.staticfiles.checks import check_finders +from django.conf import DEFAULT_STORAGE_ALIAS, STATICFILES_STORAGE_ALIAS, settings +from django.contrib.staticfiles.checks import E005, check_finders, check_storages from django.contrib.staticfiles.finders import BaseFinder, get_finder from django.core.checks import Error, Warning -from django.test import override_settings +from django.test import SimpleTestCase, override_settings from .cases import CollectionTestCase from .settings import TEST_ROOT @@ -132,3 +132,50 @@ class FindersCheckTests(CollectionTestCase): # Nonexistent directories are skipped. finder = get_finder("django.contrib.staticfiles.finders.FileSystemFinder") self.assertEqual(list(finder.list(None)), []) + + +class StoragesCheckTests(SimpleTestCase): + @override_settings(STORAGES={}) + def test_error_empty_storages(self): + # DEFAULT_STORAGE_ALIAS and STATICFILES_STORAGE_ALIAS need to be + # popped from STORAGES since UserSettingsHolder has code to maintain + # backward compatibility until 5.1 is out. + settings.STORAGES.clear() # RemovedInDjango51Warning + assert settings.STORAGES == {} # RemovedInDjango51Warning + errors = check_storages(None) + self.assertEqual(errors, [E005]) + + @override_settings( + STORAGES={ + DEFAULT_STORAGE_ALIAS: { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "example": { + "BACKEND": "ignore.me", + }, + } + ) + def test_error_missing_staticfiles(self): + # Check out the previous comment about UserSettingsHolder compat code. + settings.STORAGES.pop(STATICFILES_STORAGE_ALIAS) # RemovedInDjango51Warning + assert ( + STATICFILES_STORAGE_ALIAS not in settings.STORAGES + ) # RemovedInDjango51Warning + errors = check_storages(None) + self.assertEqual(errors, [E005]) + + @override_settings( + STORAGES={ + STATICFILES_STORAGE_ALIAS: { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + } + ) + def test_staticfiles_no_errors(self): + # Check out the previous comment about UserSettingsHolder compat code. + settings.STORAGES.pop(DEFAULT_STORAGE_ALIAS) # RemovedInDjango51Warning + assert ( + DEFAULT_STORAGE_ALIAS not in settings.STORAGES + ) # RemovedInDjango51Warning + errors = check_storages(None) + self.assertEqual(errors, [])