From 2d42e23b6d8fd76f93a96b2310b2c9dfd441d009 Mon Sep 17 00:00:00 2001
From: Brian Helba <brian.helba@kitware.com>
Date: Mon, 24 Aug 2020 15:27:22 -0400
Subject: [PATCH] Fixed #31941 -- Corrected FileField.deconstruct() with a
 callable storage.

---
 AUTHORS                          |  1 +
 django/db/models/fields/files.py |  4 +++-
 docs/releases/3.1.2.txt          |  3 ++-
 tests/file_storage/tests.py      | 13 ++++++++++++-
 4 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 5c50598ddc..386837aee6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -146,6 +146,7 @@ answer newbie questions, and generally made Django that much better:
     Brian Beck <http://blog.brianbeck.com/>
     Brian Fabian Crain <http://www.bfc.do/>
     Brian Harring <ferringb@gmail.com>
+    Brian Helba <brian.helba@kitware.com>
     Brian Ray <http://brianray.chipy.org/>
     Brian Rosner <brosner@gmail.com>
     Bruce Kroeze <https://coderseye.com/>
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index e10a5bb6d9..db2450a738 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -229,6 +229,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(
@@ -279,7 +281,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