mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #35139 -- Prevented file read after ImageField is saved to storage.
This commit is contained in:
parent
4971a9afe5
commit
9c5fe93349
@ -90,10 +90,13 @@ class FieldFile(File, AltersData):
|
||||
# to further manipulate the underlying file, as well as update the
|
||||
# associated model instance.
|
||||
|
||||
def _set_instance_attribute(self, name, content):
|
||||
setattr(self.instance, self.field.attname, name)
|
||||
|
||||
def save(self, name, content, save=True):
|
||||
name = self.field.generate_filename(self.instance, name)
|
||||
self.name = self.storage.save(name, content, max_length=self.field.max_length)
|
||||
setattr(self.instance, self.field.attname, self.name)
|
||||
self._set_instance_attribute(self.name, content)
|
||||
self._committed = True
|
||||
|
||||
# Save the object because it has changed, unless save is False
|
||||
@ -391,6 +394,12 @@ class ImageFileDescriptor(FileDescriptor):
|
||||
|
||||
|
||||
class ImageFieldFile(ImageFile, FieldFile):
|
||||
def _set_instance_attribute(self, name, content):
|
||||
setattr(self.instance, self.field.attname, content)
|
||||
# Update the name in case generate_filename() or storage.save() changed
|
||||
# it, but bypass the descriptor to avoid re-reading the file.
|
||||
self.instance.__dict__[self.field.attname] = self.name
|
||||
|
||||
def delete(self, save=True):
|
||||
# Clear the image dimensions cache
|
||||
if hasattr(self, "_dimensions_cache"):
|
||||
|
@ -437,6 +437,11 @@ Miscellaneous
|
||||
:class:`~django.core.exceptions.FieldError` when saving a file without a
|
||||
``name``.
|
||||
|
||||
* ``ImageField.update_dimension_fields(force=True)`` is no longer called after
|
||||
saving the image to storage. If your storage backend resizes images, the
|
||||
``width_field`` and ``height_field`` will not match the width and height of
|
||||
the image.
|
||||
|
||||
.. _deprecated-features-5.1:
|
||||
|
||||
Features deprecated in 5.1
|
||||
|
@ -13,6 +13,8 @@ from django.db.models.functions import Lower
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .storage import NoReadFileSystemStorage
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
@ -373,6 +375,21 @@ if Image:
|
||||
width_field="headshot_width",
|
||||
)
|
||||
|
||||
class PersonNoReadImage(models.Model):
|
||||
"""
|
||||
Model that defines an ImageField with a storage backend that does not
|
||||
support reading.
|
||||
"""
|
||||
|
||||
mugshot = models.ImageField(
|
||||
upload_to="tests",
|
||||
storage=NoReadFileSystemStorage(),
|
||||
width_field="mugshot_width",
|
||||
height_field="mugshot_height",
|
||||
)
|
||||
mugshot_width = models.IntegerField()
|
||||
mugshot_height = models.IntegerField()
|
||||
|
||||
|
||||
class CustomJSONDecoder(json.JSONDecoder):
|
||||
def __init__(self, object_hook=None, *args, **kwargs):
|
||||
|
6
tests/model_fields/storage.py
Normal file
6
tests/model_fields/storage.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.core.files.storage.filesystem import FileSystemStorage
|
||||
|
||||
|
||||
class NoReadFileSystemStorage(FileSystemStorage):
|
||||
def open(self, *args, **kwargs):
|
||||
raise AssertionError("This storage class does not support reading.")
|
@ -18,6 +18,7 @@ if Image:
|
||||
from .models import (
|
||||
Person,
|
||||
PersonDimensionsFirst,
|
||||
PersonNoReadImage,
|
||||
PersonTwoImages,
|
||||
PersonWithHeight,
|
||||
PersonWithHeightAndWidth,
|
||||
@ -30,7 +31,7 @@ else:
|
||||
pass
|
||||
|
||||
PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
|
||||
PersonTwoImages = Person
|
||||
PersonTwoImages = PersonNoReadImage = Person
|
||||
|
||||
|
||||
class ImageFieldTestMixin(SerializeMixin):
|
||||
@ -469,3 +470,28 @@ class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
|
||||
# Dimensions were recalculated, and hence file should have opened.
|
||||
self.assertIs(p.mugshot.was_opened, True)
|
||||
self.assertIs(p.headshot.was_opened, True)
|
||||
|
||||
|
||||
@skipIf(Image is None, "Pillow is required to test ImageField")
|
||||
class NoReadTests(ImageFieldTestMixin, TestCase):
|
||||
def test_width_height_correct_name_mangling_correct(self):
|
||||
instance1 = PersonNoReadImage()
|
||||
|
||||
instance1.mugshot.save("mug", self.file1)
|
||||
|
||||
self.assertEqual(instance1.mugshot_width, 4)
|
||||
self.assertEqual(instance1.mugshot_height, 8)
|
||||
|
||||
instance1.save()
|
||||
|
||||
self.assertEqual(instance1.mugshot_width, 4)
|
||||
self.assertEqual(instance1.mugshot_height, 8)
|
||||
|
||||
instance2 = PersonNoReadImage()
|
||||
instance2.mugshot.save("mug", self.file1)
|
||||
instance2.save()
|
||||
|
||||
self.assertNotEqual(instance1.mugshot.name, instance2.mugshot.name)
|
||||
|
||||
self.assertEqual(instance1.mugshot_width, instance2.mugshot_width)
|
||||
self.assertEqual(instance1.mugshot_height, instance2.mugshot_height)
|
||||
|
Loading…
Reference in New Issue
Block a user