From 49864105cdbe91a1bbc76832b65d770d2b8d944e Mon Sep 17 00:00:00 2001 From: Daniel Wong Date: Mon, 19 May 2025 16:27:56 -0600 Subject: [PATCH] feat: update file storage access to support Django 5.0 storages registry --- .../export_course_metadata/storage.py | 4 +- common/djangoapps/util/storage.py | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 common/djangoapps/util/storage.py diff --git a/cms/djangoapps/export_course_metadata/storage.py b/cms/djangoapps/export_course_metadata/storage.py index f9ebad7d41..4c74f8536c 100644 --- a/cms/djangoapps/export_course_metadata/storage.py +++ b/cms/djangoapps/export_course_metadata/storage.py @@ -4,7 +4,7 @@ Storage backend for course metadata export. from django.conf import settings -from django.core.files.storage import get_storage_class +from common.djangoapps.util.storage import resolve_storage_backend from storages.backends.s3boto3 import S3Boto3Storage @@ -17,4 +17,4 @@ class CourseMetadataExportS3Storage(S3Boto3Storage): # pylint: disable=abstract bucket = settings.COURSE_METADATA_EXPORT_BUCKET super().__init__(bucket_name=bucket, custom_domain=None, querystring_auth=True) -course_metadata_export_storage = get_storage_class(settings.COURSE_METADATA_EXPORT_STORAGE)() +course_metadata_export_storage = resolve_storage_backend("COURSE_METADATA_EXPORT_STORAGE") diff --git a/common/djangoapps/util/storage.py b/common/djangoapps/util/storage.py new file mode 100644 index 0000000000..22824b267e --- /dev/null +++ b/common/djangoapps/util/storage.py @@ -0,0 +1,47 @@ +""" Utility functions related to django storages """ + +import django +from django.conf import settings +from django.core.files.storage import default_storage +from django.utils.module_loading import import_string + + +if django.VERSION >= (5, 0): + from django.core.files.storage import storages + + +def resolve_storage_backend(storage_key, options={}): + storage_path = getattr(settings, storage_key) + storages_config = getattr(settings, 'STORAGES', {}) + + if storage_key == "default": + # Use case 1: Default storage + # Works consistently across Django 4.2 and 5.x + # In Django 4.2, default_storage uses DEFAULT_FILE_STORAGE + # In Django 5.x, it uses STORAGES['default'] + return default_storage + + if django.VERSION >= (5, 0) and storage_key in storages_config: + # Use case 2: Django 5+ with STORAGES defined + # settings.py must define STORAGES like: + # STORAGES = { + # "default": {"BACKEND": "...", "OPTIONS": {...}}, + # "custom": {"BACKEND": "...", "OPTIONS": {...}}, + # } + # See: https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STORAGES + return storages[storage_key] + + if not storage_path and storage_key in storages_config: + # Use case 3: Transitional setup + # Running on Django 4.x but using Django 5-style STORAGES config + # Manually load the backend and options + storage_path = storages_config.get(storage_key, {}).get("BACKEND") + options = storages_config.get(storage_key, {}).get("OPTIONS", {}) + + if not storage_path: + # if no specific storage was resolved, use the default storage + return default_storage + + # Fallback to import the storage_path manually + StorageClass = import_string(storage_path) + return StorageClass(**options)