From e0a0d01d265e4c0b792546ddd26097a90dc21d98 Mon Sep 17 00:00:00 2001 From: Daniel Wong Date: Tue, 3 Jun 2025 13:53:32 -0600 Subject: [PATCH] feat: update file storage access to support Django 5.0 storages registry --- cms/djangoapps/contentstore/storage.py | 7 +- .../contentstore/tests/test_import.py | 81 +++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/cms/djangoapps/contentstore/storage.py b/cms/djangoapps/contentstore/storage.py index 77efa4130e..83308ebd86 100644 --- a/cms/djangoapps/contentstore/storage.py +++ b/cms/djangoapps/contentstore/storage.py @@ -4,7 +4,7 @@ Storage backend for course import and 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 from storages.utils import setting @@ -19,4 +19,7 @@ class ImportExportS3Storage(S3Boto3Storage): # pylint: disable=abstract-method super().__init__(bucket_name=bucket, custom_domain=None, querystring_auth=True) # pylint: disable=invalid-name -course_import_export_storage = get_storage_class(settings.COURSE_IMPORT_EXPORT_STORAGE)() +course_import_export_storage = resolve_storage_backend( + storage_key="course_import_export", + legacy_setting_key="COURSE_IMPORT_EXPORT_STORAGE" +) diff --git a/cms/djangoapps/contentstore/tests/test_import.py b/cms/djangoapps/contentstore/tests/test_import.py index 260636b51b..697d829e54 100644 --- a/cms/djangoapps/contentstore/tests/test_import.py +++ b/cms/djangoapps/contentstore/tests/test_import.py @@ -21,6 +21,9 @@ from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.xml_importer import import_course_from_xml +from common.djangoapps.util.storage import resolve_storage_backend +from storages.backends.s3boto3 import S3Boto3Storage + TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE) TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex @@ -275,3 +278,81 @@ class ContentStoreImportTest(ModuleStoreTestCase): video = module_store.get_item(vertical.children[1]) self.assertEqual(video.display_name, 'default') + + @override_settings( + COURSE_IMPORT_EXPORT_STORAGE="cms.djangoapps.contentstore.storage.ImportExportS3Storage", + DEFAULT_FILE_STORAGE="django.core.files.storage.FileSystemStorage" + ) + def test_resolve_default_storage(self): + """ Ensure the default storage is invoked, even if course export storage is configured """ + storage = resolve_storage_backend( + storage_key="default", + legacy_setting_key="DEFAULT_FILE_STORAGE" + ) + self.assertEqual(storage.__class__.__name__, "FileSystemStorage") + + @override_settings( + COURSE_IMPORT_EXPORT_STORAGE="cms.djangoapps.contentstore.storage.ImportExportS3Storage", + DEFAULT_FILE_STORAGE="django.core.files.storage.FileSystemStorage", + COURSE_IMPORT_EXPORT_BUCKET="bucket_name_test" + ) + def test_resolve_happy_path_storage(self): + """ Make sure that the correct course export storage is being used """ + storage = resolve_storage_backend( + storage_key="course_import_export", + legacy_setting_key="COURSE_IMPORT_EXPORT_STORAGE" + ) + self.assertEqual(storage.__class__.__name__, "ImportExportS3Storage") + self.assertEqual(storage.bucket_name, "bucket_name_test") + + @override_settings() + def test_resolve_storage_with_no_config(self): + """ If no storage setup is defined, we get FileSystemStorage by default """ + del settings.DEFAULT_FILE_STORAGE + del settings.COURSE_IMPORT_EXPORT_STORAGE + del settings.COURSE_IMPORT_EXPORT_BUCKET + storage = resolve_storage_backend( + storage_key="course_import_export", + legacy_setting_key="COURSE_IMPORT_EXPORT_STORAGE" + ) + self.assertEqual(storage.__class__.__name__, "FileSystemStorage") + + @override_settings( + COURSE_IMPORT_EXPORT_STORAGE=None, + COURSE_IMPORT_EXPORT_BUCKET="bucket_name_test", + STORAGES={ + 'course_import_export': { + 'BACKEND': 'cms.djangoapps.contentstore.storage.ImportExportS3Storage', + 'OPTIONS': {} + } + } + ) + def test_resolve_storage_using_django5_settings(self): + """ Simulating a Django 4 environment using Django 5 Storages configuration """ + storage = resolve_storage_backend( + storage_key="course_import_export", + legacy_setting_key="COURSE_IMPORT_EXPORT_STORAGE" + ) + self.assertEqual(storage.__class__.__name__, "ImportExportS3Storage") + self.assertEqual(storage.bucket_name, "bucket_name_test") + + @override_settings( + STORAGES={ + 'course_import_export': { + 'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage', + 'OPTIONS': { + 'bucket_name': 'bucket_name_test' + } + } + } + ) + def test_resolve_storage_using_django5_settings_with_options(self): + """ Ensure we call the storage class with the correct parameters and Django 5 setup """ + del settings.COURSE_IMPORT_EXPORT_STORAGE + del settings.COURSE_IMPORT_EXPORT_BUCKET + storage = resolve_storage_backend( + storage_key="course_import_export", + legacy_setting_key="COURSE_IMPORT_EXPORT_STORAGE" + ) + self.assertEqual(storage.__class__.__name__, S3Boto3Storage.__name__) + self.assertEqual(storage.bucket_name, "bucket_name_test")