#36824: FileField storage parameter should support string references to STORAGES
dict keys
-----------------------------+-----------------------------------------
     Reporter:  Petr Dlouhý  |                     Type:  New feature
       Status:  new          |                Component:  Uncategorized
      Version:  6.0          |                 Severity:  Normal
     Keywords:               |             Triage Stage:  Unreviewed
    Has patch:  0            |      Needs documentation:  0
  Needs tests:  0            |  Patch needs improvement:  0
Easy pickings:  0            |                    UI/UX:  0
-----------------------------+-----------------------------------------
 = Summary =

 FileField's `storage` parameter doesn't support string references to
 `STORAGES` dict keys, despite Django 4.2+ establishing STORAGES as the
 standard configuration method. This creates confusion and forces
 workarounds.

 = Description =

 Django 4.2 introduced the `STORAGES` setting as a centralized dictionary
 for configuring storage backends, deprecating `DEFAULT_FILE_STORAGE`.
 However, `FileField` and `ImageField` don't support string references to
 these storage keys.

 == Current Behavior ==

 {{{
 #!python
 # settings.py
 STORAGES = {
     "default": {...},
     "private_assets": {...},
 }

 # models.py
 from django.core.files.storage import storages

 # This works - but verbose and requires callable wrapper
 def get_private_storage():
     return storages["private_assets"]

 class MyModel(models.Model):
     file = models.FileField(storage=get_private_storage)
 }}}

 == Expected Behavior ==

 {{{
 #!python
 # This should work - consistent with STORAGES design
 class MyModel(models.Model):
     file = models.FileField(storage="private_assets")
 }}}

 == Problem ==

 When developers try the intuitive approach (`storage="private_assets"`),
 Django stores it as a string. This fails silently until runtime when code
 accesses `field.storage`, causing:

 {{{
 AttributeError: 'str' object has no attribute 'path'
 }}}

 This is particularly problematic for third-party libraries (like `easy-
 thumbnails`) that access `field.storage` directly and expect a storage
 object, not a string.

 = Inconsistency =

 The `STORAGES` dict uses string keys everywhere:
  * ✅ `settings.STORAGES["default"]` - configuration
  * ✅ `storages["private_assets"]` - runtime access
  * ✅ `THUMBNAIL_DEFAULT_STORAGE = "thumbnails"` - library settings
  * ❌ `FileField(storage="name")` - '''doesn't work'''

 This breaks the principle of least surprise. If Django establishes a
 pattern (string keys for storage), it should work consistently across the
 framework.

 = Real-World Impact =

 I encountered this while implementing Django STORAGES support for `easy-
 thumbnails`. Users naturally try `storage="name"` based on the STORAGES
 pattern, and it fails with a cryptic error. This forced me to add
 defensive string resolution in the library.

 = Proposed Solutions =

 '''Option A: Support string storage in FileField (Recommended)'''

 Add string resolution in `FileField.__init__()`:

 {{{
 #!python
 def __init__(self, ..., storage=None, **kwargs):
     self.storage = storage or default_storage

     # Resolve string references to STORAGES dict
     if isinstance(self.storage, str):
         from django.core.files.storage import storages
         self.storage = storages[self.storage]

     if callable(self.storage):
         self._storage_callable = self.storage
         self.storage = self.storage()
         ...
 }}}

 '''Benefits:'''
  * Consistent with STORAGES design
  * Intuitive for developers
  * Backwards compatible (no breaking changes)
  * ~5 lines of code

 '''Option B: Document that string storage is not supported'''

 If supporting string storage is not desired, clearly document in
 `FileField` docs:

 {{{
 Note: The storage parameter does not support string references to STORAGES
 dict keys.
 Use a callable function to reference configured storages:

 def get_my_storage():
     from django.core.files.storage import storages
     return storages["my_storage"]

 file = models.FileField(storage=get_my_storage)
 }}}

 This at least prevents confusion and provides a clear workaround.

 = Migration Behavior =

 String storage resolution wouldn't create migration issues because:
  * Strings would be resolved at model load time (like callables)
  * Migrations would store the string reference, not the resolved object
  * This mirrors how callable storage already works

 = Backwards Compatibility =

 Fully backwards compatible - existing code using storage objects or
 callables continues working unchanged.

 = Related Tickets =

  * #31941 - FileField with callable storage deconstruction
  * #34192 - Callable storage returning default_storage

 = Questions =

  1. Was excluding string storage from FileField intentional, or an
 oversight during STORAGES implementation?
  2. If intentional, what's the reasoning? (I'd like to understand the
 design decision)
  3. If not desired as a feature, can we at least improve documentation to
 prevent this confusion?
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36824>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019b4bacc321-f0e91e14-2976-41b4-80eb-655636b0040e-000000%40eu-central-1.amazonses.com.

Reply via email to