From 210657b791fec359a9109b69e566018253edfad0 Mon Sep 17 00:00:00 2001 From: miigotu Date: Tue, 31 Mar 2020 12:12:39 +0200 Subject: Fixed #28184 -- Allowed using a callable for FileField and ImageField storage. --- tests/file_storage/models.py | 12 ++++++++++++ tests/file_storage/tests.py | 45 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/file_storage/models.py b/tests/file_storage/models.py index 8085a8bb1a..9097f465dd 100644 --- a/tests/file_storage/models.py +++ b/tests/file_storage/models.py @@ -23,6 +23,16 @@ temp_storage_location = tempfile.mkdtemp() temp_storage = FileSystemStorage(location=temp_storage_location) +def callable_storage(): + return temp_storage + + +class CallableStorage(FileSystemStorage): + def __call__(self): + # no-op implementation. + return self + + class Storage(models.Model): def custom_upload_to(self, filename): return 'foo' @@ -44,6 +54,8 @@ class Storage(models.Model): storage=CustomValidNameStorage(location=temp_storage_location), upload_to=random_upload_to, ) + storage_callable = models.FileField(storage=callable_storage, upload_to='storage_callable') + storage_callable_class = models.FileField(storage=CallableStorage, upload_to='storage_callable_class') default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') empty = models.FileField(storage=temp_storage) limited_length = models.FileField(storage=temp_storage, upload_to='tests', max_length=20) diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 9b41f5250d..a1d2bd372c 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -13,10 +13,13 @@ from urllib.request import urlopen from django.core.cache import cache from django.core.exceptions import SuspiciousFileOperation from django.core.files.base import ContentFile, File -from django.core.files.storage import FileSystemStorage, get_storage_class +from django.core.files.storage import ( + FileSystemStorage, Storage as BaseStorage, get_storage_class, +) from django.core.files.uploadedfile import ( InMemoryUploadedFile, SimpleUploadedFile, TemporaryUploadedFile, ) +from django.db.models import FileField from django.db.models.fields.files import FileDescriptor from django.test import ( LiveServerTestCase, SimpleTestCase, TestCase, override_settings, @@ -866,6 +869,46 @@ class FileFieldStorageTests(TestCase): self.assertEqual(f.read(), b'content') +class FieldCallableFileStorageTests(SimpleTestCase): + def setUp(self): + self.temp_storage_location = tempfile.mkdtemp(suffix='filefield_callable_storage') + + def tearDown(self): + shutil.rmtree(self.temp_storage_location) + + def test_callable_base_class_error_raises(self): + class NotStorage: + pass + msg = 'FileField.storage must be a subclass/instance of django.core.files.storage.Storage' + for invalid_type in (NotStorage, str, list, set, tuple): + with self.subTest(invalid_type=invalid_type): + with self.assertRaisesMessage(TypeError, msg): + FileField(storage=invalid_type) + + def test_callable_function_storage_file_field(self): + storage = FileSystemStorage(location=self.temp_storage_location) + + def get_storage(): + return storage + + obj = FileField(storage=get_storage) + self.assertEqual(obj.storage, storage) + self.assertEqual(obj.storage.location, storage.location) + + def test_callable_class_storage_file_field(self): + class GetStorage(FileSystemStorage): + pass + + obj = FileField(storage=GetStorage) + self.assertIsInstance(obj.storage, BaseStorage) + + def test_callable_storage_file_field_in_model(self): + obj = Storage() + self.assertEqual(obj.storage_callable.storage, temp_storage) + self.assertEqual(obj.storage_callable.storage.location, temp_storage_location) + self.assertIsInstance(obj.storage_callable_class.storage, BaseStorage) + + # Tests for a race condition on file saving (#4948). # This is written in such a way that it'll always pass on platforms # without threading. -- cgit v1.2.1