Opened 3 weeks ago
Closed 4 days ago
#36622 closed Bug (fixed)
FileField.__init__ is triggering storage LazyObject resolution at boot time
Reported by: | Fabien MICHEL | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 5.2 |
Severity: | Normal | Keywords: | |
Cc: | Fabien MICHEL | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
There is this code in FileField.__init__
:
self.storage = storage or default_storage
The problem is that if we want to have a LazyObject as storage for the field, it is still resolved at boot time, and it seems unecessary to trigger resolution at this moment. This seems not expected as the documentation itself suggest to use LazyObject in case we require a callable to be overridable during tests : https://docs.djangoproject.com/en/5.2/topics/files/#using-a-callable
Using storage or default_storage
is calling storage.__bool__
which trigger resolution on default LazyObject.
A solution would be to change this line by :
self.storage = storage if storage is not None else default_storage
A workaround for the case specified in the documentation is to override __bool__
on LazyObject
so it always returns True.
class OtherStorage(LazyObject): def _setup(self): self._wrapped = storages["mystorage"] def __bool__(self): return True
Change History (9)
comment:1 by , 3 weeks ago
Type: | Uncategorized → Bug |
---|
comment:2 by , 3 weeks ago
Description: | modified (diff) |
---|
comment:3 by , 3 weeks ago
comment:4 by , 3 weeks ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
Thank you for the ticket! I have tested and I agree with this approach.
Given there were some efforts started in #36504, I have added a comment there and marking this as a duplicate to try and prevent contradictory/overlapping effort
comment:5 by , 10 days ago
Resolution: | duplicate |
---|---|
Status: | closed → new |
Reopening because this issue can be fixed independently of #36504--it neither contradicts nor, strictly speaking, overlap. The reason being that for override_settings
to work on custom storages, we still need to write a custom signal receiver. It only seems to work now because we're resolving for the first time after calling override_settings
. However, whatever value is accessed will persist and cannot be overridden again.
The following modification to the docs example should demonstrate:
def test_storage(self): model = MyModel() self.assertIsInstance(model.upload.storage, FileSystemStorage) # Assuming this is in settings with override_settings( STORAGES={ "mystorage": { "BACKEND": "django.core.files.storage.InMemoryStorage", } } ): self.assertIsInstance(model.upload.storage, InMemoryStorage)
comment:6 by , 10 days ago
Has patch: | set |
---|
comment:7 by , 4 days ago
Triage Stage: | Unreviewed → Accepted |
---|
Thank you Clifford for your detailed explanation.
comment:8 by , 4 days ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:9 by , 4 days ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Fixed in main
in 6862d46dd96d71d80d6d2fa9873a93d811b39562.
Fixed in [6.0.x]
in 5c114ce2d8479549ee9dd486bc28df5b2e3d54ad.
Related #36504