Fixed #31941 -- Corrected FileField.deconstruct() with a callable storage.

This commit is contained in:
Brian Helba 2020-08-24 15:27:22 -04:00 committed by Carlton Gibson
parent ece18207cb
commit 2d42e23b6d
4 changed files with 18 additions and 3 deletions

View File

@ -146,6 +146,7 @@ answer newbie questions, and generally made Django that much better:
Brian Beck <http://blog.brianbeck.com/> Brian Beck <http://blog.brianbeck.com/>
Brian Fabian Crain <http://www.bfc.do/> Brian Fabian Crain <http://www.bfc.do/>
Brian Harring <ferringb@gmail.com> Brian Harring <ferringb@gmail.com>
Brian Helba <brian.helba@kitware.com>
Brian Ray <http://brianray.chipy.org/> Brian Ray <http://brianray.chipy.org/>
Brian Rosner <brosner@gmail.com> Brian Rosner <brosner@gmail.com>
Bruce Kroeze <https://coderseye.com/> Bruce Kroeze <https://coderseye.com/>

View File

@ -229,6 +229,8 @@ class FileField(Field):
self.storage = storage or default_storage self.storage = storage or default_storage
if callable(self.storage): if callable(self.storage):
# Hold a reference to the callable for deconstruct().
self._storage_callable = self.storage
self.storage = self.storage() self.storage = self.storage()
if not isinstance(self.storage, Storage): if not isinstance(self.storage, Storage):
raise TypeError( raise TypeError(
@ -279,7 +281,7 @@ class FileField(Field):
del kwargs["max_length"] del kwargs["max_length"]
kwargs['upload_to'] = self.upload_to kwargs['upload_to'] = self.upload_to
if self.storage is not default_storage: if self.storage is not default_storage:
kwargs['storage'] = self.storage kwargs['storage'] = getattr(self, '_storage_callable', self.storage)
return name, path, args, kwargs return name, path, args, kwargs
def get_internal_type(self): def get_internal_type(self):

View File

@ -9,4 +9,5 @@ Django 3.1.2 fixes several bugs in 3.1.1.
Bugfixes Bugfixes
======== ========
* ... * Fixed a bug in Django 3.1 where ``FileField`` instances with a callable
storage were not correctly deconstructed (:ticket:`31941`).

View File

@ -29,7 +29,9 @@ from django.test.utils import requires_tz_support
from django.urls import NoReverseMatch, reverse_lazy from django.urls import NoReverseMatch, reverse_lazy
from django.utils import timezone from django.utils import timezone
from .models import Storage, temp_storage, temp_storage_location from .models import (
Storage, callable_storage, temp_storage, temp_storage_location,
)
FILE_SUFFIX_REGEX = '[A-Za-z0-9]{7}' FILE_SUFFIX_REGEX = '[A-Za-z0-9]{7}'
@ -912,6 +914,15 @@ class FieldCallableFileStorageTests(SimpleTestCase):
self.assertEqual(obj.storage_callable.storage.location, temp_storage_location) self.assertEqual(obj.storage_callable.storage.location, temp_storage_location)
self.assertIsInstance(obj.storage_callable_class.storage, BaseStorage) self.assertIsInstance(obj.storage_callable_class.storage, BaseStorage)
def test_deconstruction(self):
"""
Deconstructing gives the original callable, not the evaluated value.
"""
obj = Storage()
*_, kwargs = obj._meta.get_field('storage_callable').deconstruct()
storage = kwargs['storage']
self.assertIs(storage, callable_storage)
# Tests for a race condition on file saving (#4948). # Tests for a race condition on file saving (#4948).
# This is written in such a way that it'll always pass on platforms # This is written in such a way that it'll always pass on platforms