Repository: django-compressor/django-compressor

Issue:
Django's `get_storage_class` is deprecated in 4.2 (Django 5.1 compat.)
Got the following warning running tests in Django 4.2:

```
/usr/local/lib/python3.11/site-packages/django/core/files/storage/__init__.py:32 RemovedInDjango51Warning: django.core.files.storage.get_storage_class is deprecated in favor of using django.core.files.storage.storages.
```




Discussion:
Hi @AngellusMortis thanks for the report. 

The progress here would be to make a compatibility shim using the new approach on 4.2+, which we'd drop when that's the minimum supported version. 

Is that something you'd like to have a go at doing? 
This won't be deprecated until 5.1, so I don't think this should block Django 4.2 compat just yet
Yep. It's just a warning at this stage. 

```pytb
  File ".../compressor/storage.py", line 115, in _setup
    self._wrapped = get_storage_class(settings.COMPRESS_STORAGE)()
  File ".../django/core/files/storage/__init__.py", line 32, in get_storage_class
    warnings.warn(GET_STORAGE_CLASS_DEPRECATED_MSG, RemovedInDjango51Warning)
django.utils.deprecation.RemovedInDjango51Warning: django.core.files.storage.get_storage_class is deprecated in favor of using django.core.files.storage.storages.
```
Hi - I've just had a go at creating a shim in #1216, please let me know if there are any issues with it!
#1244 was a duplicate. Copying comment from there: 

> We need to adjust how we reference the storages. There are a couple of PRs pointing in that direction, but we need to sit down and decide the pattern.
>
> My current thinking is that we need to add a COMPRESS_STORAGE_ALIAS setting, and then have compressor use that if defined, or add an entry using COMPRESS_STORAGE if not, probably at the moment of populating the AppRegistry, i.e. when INSTALLED_APPS is processed.
>
> It's on my radar to address this (if still pending) after DjangoCon Europe, so in the next few weeks.

Resolving patch:
diff --git a/compressor/conf.py b/compressor/conf.py
--- a/compressor/conf.py
+++ b/compressor/conf.py
@@ -16,6 +16,7 @@ class CompressorConf(AppConf):
     # the backend to use when parsing the JavaScript or Stylesheet files
     PARSER = "compressor.parser.AutoSelectParser"
     OUTPUT_DIR = "CACHE"
+    STORAGE_ALIAS = "compressor"
     STORAGE = "compressor.storage.CompressorFileStorage"
 
     COMPRESSORS = dict(
@@ -75,6 +76,7 @@ class CompressorConf(AppConf):
     OFFLINE_CONTEXT = {}
     # The name of the manifest file (e.g. filename.ext)
     OFFLINE_MANIFEST = "manifest.json"
+    OFFLINE_MANIFEST_STORAGE_ALIAS = "compressor-offine"
     OFFLINE_MANIFEST_STORAGE = "compressor.storage.OfflineManifestFileStorage"
     # The Context to be used when TemplateFilter is used
     TEMPLATE_FILTER_CONTEXT = {}
diff --git a/compressor/storage.py b/compressor/storage.py
--- a/compressor/storage.py
+++ b/compressor/storage.py
@@ -4,12 +4,25 @@
 import time
 from urllib.parse import urljoin
 
-from django.core.files.storage import FileSystemStorage, get_storage_class
+from django.core.files.storage import FileSystemStorage, InvalidStorageError, storages
 from django.utils.functional import LazyObject, SimpleLazyObject
 
 from compressor.conf import settings
 
 
+def get_storage(
+    alias=settings.COMPRESS_STORAGE_ALIAS,
+    storage_class=settings.COMPRESS_STORAGE
+):
+    try:
+        return storages[alias]
+    except InvalidStorageError:
+        storages.backends[alias] = {
+            "BACKEND": storage_class
+        }
+        return storages[alias]
+
+
 class CompressorFileStorage(FileSystemStorage):
     """
     Standard file system storage for files handled by django-compressor.
@@ -48,7 +61,7 @@ def save(self, filename, content):
 
 
 compressor_file_storage = SimpleLazyObject(
-    lambda: get_storage_class("compressor.storage.CompressorFileStorage")()
+    lambda: get_storage()
 )
 
 
@@ -112,7 +125,7 @@ def save(self, filename, content):
 
 class DefaultStorage(LazyObject):
     def _setup(self):
-        self._wrapped = get_storage_class(settings.COMPRESS_STORAGE)()
+        self._wrapped = get_storage()
 
 
 default_storage = DefaultStorage()
@@ -131,7 +144,10 @@ def __init__(self, location=None, base_url=None, *args, **kwargs):
 
 class DefaultOfflineManifestStorage(LazyObject):
     def _setup(self):
-        self._wrapped = get_storage_class(settings.COMPRESS_OFFLINE_MANIFEST_STORAGE)()
+        self._wrapped = get_storage(
+            alias=settings.COMPRESS_OFFLINE_MANIFEST_STORAGE_ALIAS,
+            storage_class=settings.COMPRESS_OFFLINE_MANIFEST_STORAGE,
+        )
 
 
 default_offline_manifest_storage = DefaultOfflineManifestStorage()
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -148,6 +148,7 @@ def find_package_data(
         "Framework :: Django",
         "Framework :: Django :: 4.2",
         "Framework :: Django :: 5.0",
+        "Framework :: Django :: 5.1",
         "Intended Audience :: Developers",
         "License :: OSI Approved :: MIT License",
         "Operating System :: OS Independent",


Test patch:
diff --git a/compressor/tests/test_storages.py b/compressor/tests/test_storages.py
--- a/compressor/tests/test_storages.py
+++ b/compressor/tests/test_storages.py
@@ -2,7 +2,7 @@
 import brotli
 
 from django.core.files.base import ContentFile
-from django.core.files.storage import get_storage_class
+from django.core.files.storage import storages
 from django.test import TestCase
 from django.test.utils import override_settings
 from django.utils.functional import LazyObject
@@ -15,16 +15,16 @@
 
 class GzipStorage(LazyObject):
     def _setup(self):
-        self._wrapped = get_storage_class(
-            "compressor.storage.GzipCompressorFileStorage"
-        )()
+        self._wrapped = storages.create_storage({
+            "BACKEND": "compressor.storage.GzipCompressorFileStorage"
+        })
 
 
 class BrotliStorage(LazyObject):
     def _setup(self):
-        self._wrapped = get_storage_class(
-            "compressor.storage.BrotliCompressorFileStorage"
-        )()
+        self._wrapped = storages.create_storage({
+            "BACKEND": "compressor.storage.BrotliCompressorFileStorage"
+        })
 
 
 @override_settings(COMPRESS_ENABLED=True)