diff --git a/AUTHORS b/AUTHORS index bcf23acb16..80748781cb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -144,6 +144,7 @@ answer newbie questions, and generally made Django that much better: Brian Beck Brian Fabian Crain Brian Harring + Brian Helba Brian Ray Brian Rosner Bruce Kroeze diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index b682db414c..3a0bfacda2 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -235,6 +235,8 @@ class FileField(Field): self.storage = storage or default_storage if callable(self.storage): + # Hold a reference to the callable for deconstruct(). + self._storage_callable = self.storage self.storage = self.storage() if not isinstance(self.storage, Storage): raise TypeError( @@ -285,7 +287,7 @@ class FileField(Field): del kwargs["max_length"] kwargs['upload_to'] = self.upload_to if self.storage is not default_storage: - kwargs['storage'] = self.storage + kwargs['storage'] = getattr(self, '_storage_callable', self.storage) return name, path, args, kwargs def get_internal_type(self): diff --git a/docs/releases/3.1.2.txt b/docs/releases/3.1.2.txt index 64c64f7e78..1243654bfb 100644 --- a/docs/releases/3.1.2.txt +++ b/docs/releases/3.1.2.txt @@ -9,4 +9,5 @@ Django 3.1.2 fixes several bugs in 3.1.1. Bugfixes ======== -* ... +* Fixed a bug in Django 3.1 where ``FileField`` instances with a callable + storage were not correctly deconstructed (:ticket:`31941`). diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 4bac3ca11d..6d17a7118b 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -29,7 +29,9 @@ from django.test.utils import requires_tz_support from django.urls import NoReverseMatch, reverse_lazy 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}' @@ -912,6 +914,15 @@ class FieldCallableFileStorageTests(SimpleTestCase): self.assertEqual(obj.storage_callable.storage.location, temp_storage_location) 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). # This is written in such a way that it'll always pass on platforms