From 2b9fb55231a3b2d5d60929bb2265bf8f46e9275a Mon Sep 17 00:00:00 2001 From: Daniel Wong Date: Fri, 13 Jun 2025 13:53:48 -0600 Subject: [PATCH] feat: deprecate get_storage_class --- common/djangoapps/util/storage.py | 38 +++++++++---------- lms/djangoapps/verify_student/models.py | 9 ++++- .../content/block_structure/models.py | 11 ++++-- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/common/djangoapps/util/storage.py b/common/djangoapps/util/storage.py index 3474b07c07..7d419d8fb2 100644 --- a/common/djangoapps/util/storage.py +++ b/common/djangoapps/util/storage.py @@ -1,46 +1,49 @@ """ Utility functions related to django storages """ -from typing import Optional +from typing import Optional, List from django.conf import settings -from django.core.files.storage import default_storage, storages +from django.core.files.storage import storages from django.utils.module_loading import import_string -def resolve_storage_backend(storage_key: str, legacy_setting_key: str, options: Optional[dict] = None): +def resolve_storage_backend( + storage_key: str, + legacy_setting_key: str, + legacy_sec_setting_keys: List[str] = None, + options: Optional[dict] = None): """ Configures and returns a Django `Storage` instance, compatible with both Django 4 and Django 5. Params: storage_key = The key name saved in Django storages settings. legacy_setting_key = The key name saved in Django settings. + legacy_sec_setting_keys = List of keys to get the storage class. + For legacy dict settings like settings.BLOCK_STRUCTURES_SETTINGS.get('STORAGE_CLASS'), + it is necessary to access a second-level key or above to retrieve the class path. + legacy_options = Kwargs for the storage class. options = Kwargs for the storage class. Returns: An instance of the configured storage backend. Raises: ImportError: If the specified storage class cannot be imported. - Main goal: Deprecate the use of `django.core.files.storage.get_storage_class`. - How: Replace `get_storage_class` with direct configuration logic, ensuring backward compatibility with both Django 4 and Django 5 storage settings. """ storage_path = getattr(settings, legacy_setting_key, None) + if isinstance(storage_path, dict) and legacy_sec_setting_keys: + for deep_setting_key in legacy_sec_setting_keys: + # For legacy dict settings like settings.CUSTOM_STORAGE = {"BACKEND": "cms.custom.."} + storage_path = storage_path.get(deep_setting_key) storages_config = getattr(settings, 'STORAGES', {}) if options is None: options = {} - if storage_key == "default": - # Use case 1: Default storage - # Works consistently across Django 4.2 and 5.x. - # In Django 4.2 and above, `default_storage` uses - # either `DEFAULT_FILE_STORAGE` or `STORAGES['default']`. - return default_storage - if storage_key in storages_config: - # Use case 2: STORAGES is defined + # Use case 1: STORAGES is defined # If STORAGES is present, we retrieve it through the storages API # settings.py must define STORAGES like: # STORAGES = { @@ -50,12 +53,7 @@ def resolve_storage_backend(storage_key: str, legacy_setting_key: str, options: # See: https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STORAGES return storages[storage_key] - if not storage_path: - # Use case 3: No storage settings defined - # If no storage settings are defined anywhere, use the default storage - return default_storage - - # Use case 4: Legacy settings + # Use case 2: Legacy settings # Fallback to import the storage_path (Obtained from django settings) manually - StorageClass = import_string(storage_path) + StorageClass = import_string(storage_path or settings.DEFAULT_FILE_STORAGE) return StorageClass(**options) diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 1c10cf7d0a..a483d765d0 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -44,8 +44,8 @@ from lms.djangoapps.verify_student.ssencrypt import ( rsa_decrypt, rsa_encrypt ) +from openedx.core.djangoapps.content.block_structure.models import resolve_storage_backend from openedx.core.djangoapps.signals.signals import LEARNER_SSO_VERIFIED, PHOTO_VERIFICATION_APPROVED -from openedx.core.storage import get_storage from .utils import auto_verify_for_testing_enabled, earliest_allowed_verification_date, submit_request_to_ss @@ -926,7 +926,12 @@ class SoftwareSecurePhotoVerification(PhotoVerification): storage_kwargs["bucket_name"] = config["S3_BUCKET"] storage_kwargs["querystring_expire"] = self.IMAGE_LINK_DURATION - return get_storage(storage_class, **storage_kwargs) + return resolve_storage_backend( + storage_key="software_secure", + legacy_setting_key="SOFTWARE_SECURE", + legacy_sec_setting_key="STORAGE_CLASS", + options=storage_kwargs + ) def _get_path(self, prefix, override_receipt_id=None): """ diff --git a/openedx/core/djangoapps/content/block_structure/models.py b/openedx/core/djangoapps/content/block_structure/models.py index b3a8439ca1..9099f83274 100644 --- a/openedx/core/djangoapps/content/block_structure/models.py +++ b/openedx/core/djangoapps/content/block_structure/models.py @@ -13,10 +13,10 @@ from django.core.exceptions import SuspiciousOperation from django.core.files.base import ContentFile from django.db import models, transaction +from common.djangoapps.util.storage import resolve_storage_backend from model_utils.models import TimeStampedModel from openedx.core.djangoapps.xmodule_django.models import UsageKeyWithRunField -from openedx.core.storage import get_storage from . import config from .exceptions import BlockStructureNotFound @@ -73,7 +73,7 @@ def _bs_model_storage(): # .. setting_default: None # .. setting_description: Specifies the storage used for storage-backed block structure cache. # For more information, check https://github.com/openedx/edx-platform/pull/14571. - storage_class = settings.BLOCK_STRUCTURES_SETTINGS.get('STORAGE_CLASS') + # storage_class = settings.BLOCK_STRUCTURES_SETTINGS.get('STORAGE_CLASS') # .. setting_name: BLOCK_STRUCTURES_SETTINGS['STORAGE_KWARGS'] # .. setting_default: {} @@ -83,7 +83,12 @@ def _bs_model_storage(): # .. setting_warnings: Depends on `BLOCK_STRUCTURES_SETTINGS['STORAGE_CLASS']` storage_kwargs = settings.BLOCK_STRUCTURES_SETTINGS.get('STORAGE_KWARGS', {}) - return get_storage(storage_class, **storage_kwargs) + return resolve_storage_backend( + storage_key="block_structures_settings", + legacy_setting_key="BLOCK_STRUCTURES_SETTINGS", + legacy_sec_setting_key="STORAGE_CLASS", + options=storage_kwargs + ) class CustomizableFileField(models.FileField):