Skip to content

Commit

Permalink
Refactor how we get the default storage class
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexerson committed May 9, 2023
1 parent e86a40b commit 5cdcbb2
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 17 deletions.
13 changes: 12 additions & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,23 @@ Settings

:default: ``None``

Starting with Django 4.2, if you defined ``settings.STORAGES``:
the Django storage backend alias to retrieve the storage instance defined
in your settings, as described in the `Django file storage documentation`_.
If no value is provided for ``IMAGEKIT_DEFAULT_FILE_STORAGE``,
and none is specified by the spec definition, the ``default`` file storage
will be used.

Before Django 4.2, or if ``settings.STORAGES`` is not defined:
The qualified class name of a Django storage backend to use to save the
cached images. If no value is provided for ``IMAGEKIT_DEFAULT_FILE_STORAGE``,
and none is specified by the spec definition, `your default file storage`__
will be used.


.. _`Django file storage documentation`: https://docs.djangoproject.com/en/dev/ref/files/storage/


.. attribute:: IMAGEKIT_DEFAULT_CACHEFILE_BACKEND

:default: ``'imagekit.cachefiles.backends.Simple'``
Expand All @@ -52,7 +63,7 @@ Settings
The cache is then used to store information like the state of cached
images (i.e. validated or not).

.. _`Django cache section`: https://docs.djangoproject.com/en/1.8/topics/cache/#accessing-the-cache
.. _`Django cache section`: https://docs.djangoproject.com/en/dev/topics/cache/#accessing-the-cache


.. attribute:: IMAGEKIT_CACHE_TIMEOUT
Expand Down
17 changes: 6 additions & 11 deletions imagekit/cachefiles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from ..files import BaseIKFile
from ..registry import generator_registry
from ..signals import content_required, existence_required
from ..utils import generate, get_by_qname, get_logger, get_singleton
from ..utils import (
generate, get_by_qname, get_logger, get_singleton, get_storage
)


class ImageCacheFile(BaseIKFile, ImageFile):
Expand Down Expand Up @@ -44,8 +46,7 @@ def __init__(self, generator, name=None, storage=None, cachefile_backend=None, c
self.name = name

storage = (callable(storage) and storage()) or storage or \
getattr(generator, 'cachefile_storage', None) or get_singleton(
settings.IMAGEKIT_DEFAULT_FILE_STORAGE, 'file storage backend')
getattr(generator, 'cachefile_storage', None) or get_storage()
self.cachefile_backend = (
cachefile_backend
or getattr(generator, 'cachefile_backend', None)
Expand Down Expand Up @@ -156,20 +157,14 @@ def __getstate__(self):

# remove storage from state as some non-FileSystemStorage can't be
# pickled
settings_storage = get_singleton(
settings.IMAGEKIT_DEFAULT_FILE_STORAGE,
'file storage backend'
)
settings_storage = get_storage()
if state['storage'] == settings_storage:
state.pop('storage')
return state

def __setstate__(self, state):
if 'storage' not in state:
state['storage'] = get_singleton(
settings.IMAGEKIT_DEFAULT_FILE_STORAGE,
'file storage backend'
)
state['storage'] = get_storage()
self.__dict__.update(state)

def __repr__(self):
Expand Down
8 changes: 5 additions & 3 deletions imagekit/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def configure_cache_timeout(self, value):
def configure_default_file_storage(self, value):
if value is None:
try:
value = settings.STORAGES["default"]["BACKEND"]
except AttributeError:
value = settings.DEFAULT_FILE_STORAGE
from django.conf import DEFAULT_STORAGE_ALIAS
except ImportError: # Django < 4.2
return settings.DEFAULT_FILE_STORAGE
else:
return DEFAULT_STORAGE_ALIAS
return value
16 changes: 16 additions & 0 deletions imagekit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ def get_cache():
return caches[settings.IMAGEKIT_CACHE_BACKEND]


def get_storage():
try:
from django.core.files.storage import storages, InvalidStorageError
except ImportError: # Django < 4.2
return get_singleton(
settings.IMAGEKIT_DEFAULT_FILE_STORAGE, 'file storage backend'
)
else:
try:
return storages[settings.IMAGEKIT_DEFAULT_FILE_STORAGE]
except InvalidStorageError:
return get_singleton(
settings.IMAGEKIT_DEFAULT_FILE_STORAGE, 'file storage backend'
)


def sanitize_cache_key(key):
if settings.IMAGEKIT_USE_MEMCACHED_SAFE_CACHE_KEY:
# Memcached keys can't contain whitespace or control characters.
Expand Down
46 changes: 44 additions & 2 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.test import override_settings
import pytest
from imagekit.conf import ImageKitConf, settings
from imagekit.utils import get_storage


@pytest.mark.skipif(
Expand All @@ -17,7 +18,7 @@ def test_custom_storages():
},
):
conf = ImageKitConf()
assert conf.configure_default_file_storage(None) == "tests.utils.CustomStorage"
assert conf.configure_default_file_storage(None) == "default"


@pytest.mark.skipif(
Expand All @@ -29,4 +30,45 @@ def test_custom_default_file_storage():
# If we don’t remove this, Django 4.2 will keep the old value.
del settings.STORAGES
conf = ImageKitConf()
assert conf.configure_default_file_storage(None) == "tests.utils.CustomStorage"

if django.VERSION >= (4, 2):
assert conf.configure_default_file_storage(None) == "default"
else:
assert (
conf.configure_default_file_storage(None) == "tests.utils.CustomStorage"
)


def test_get_storage_default():
from django.core.files.storage import FileSystemStorage

assert isinstance(get_storage(), FileSystemStorage)


@pytest.mark.skipif(
django.VERSION >= (5, 1),
reason="DEFAULT_FILE_STORAGE is removed in Django 5.1.",
)
def test_get_storage_custom_path():
from tests.utils import CustomStorage

with override_settings(IMAGEKIT_DEFAULT_FILE_STORAGE="tests.utils.CustomStorage"):
assert isinstance(get_storage(), CustomStorage)


@pytest.mark.skipif(
django.VERSION < (4, 2),
reason="STORAGES was introduced in Django 4.2",
)
def test_get_storage_custom_key():
from tests.utils import CustomStorage

with override_settings(
STORAGES={
"custom": {
"BACKEND": "tests.utils.CustomStorage",
}
},
IMAGEKIT_DEFAULT_FILE_STORAGE="custom",
):
assert isinstance(get_storage(), CustomStorage)
42 changes: 42 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import django
from django.test import override_settings
import pytest
from imagekit.utils import get_storage


def test_get_storage_default():
from django.core.files.storage import default_storage

if django.VERSION >= (4, 2):
assert get_storage() == default_storage
else:
assert isinstance(get_storage(), type(default_storage._wrapped))


@pytest.mark.skipif(
django.VERSION >= (5, 1),
reason="DEFAULT_FILE_STORAGE is removed in Django 5.1.",
)
def test_get_storage_custom_import_path():
from tests.utils import CustomStorage

with override_settings(IMAGEKIT_DEFAULT_FILE_STORAGE="tests.utils.CustomStorage"):
assert isinstance(get_storage(), CustomStorage)


@pytest.mark.skipif(
django.VERSION < (4, 2),
reason="STORAGES was introduced in Django 4.2",
)
def test_get_storage_custom_key():
from tests.utils import CustomStorage

with override_settings(
STORAGES={
"custom": {
"BACKEND": "tests.utils.CustomStorage",
}
},
IMAGEKIT_DEFAULT_FILE_STORAGE="custom",
):
assert isinstance(get_storage(), CustomStorage)

0 comments on commit 5cdcbb2

Please sign in to comment.