mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +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
|
# to further manipulate the underlying file, as well as update the
|
||||||
# associated model instance.
|
# associated model instance.
|
||||||
|
|
||||||
|
def _set_instance_attribute(self, name, content):
|
||||||
|
setattr(self.instance, self.field.attname, name)
|
||||||
|
|
||||||
def save(self, name, content, save=True):
|
def save(self, name, content, save=True):
|
||||||
name = self.field.generate_filename(self.instance, name)
|
name = self.field.generate_filename(self.instance, name)
|
||||||
self.name = self.storage.save(name, content, max_length=self.field.max_length)
|
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
|
self._committed = True
|
||||||
|
|
||||||
# Save the object because it has changed, unless save is False
|
# Save the object because it has changed, unless save is False
|
||||||
@ -391,6 +394,12 @@ class ImageFileDescriptor(FileDescriptor):
|
|||||||
|
|
||||||
|
|
||||||
class ImageFieldFile(ImageFile, FieldFile):
|
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):
|
def delete(self, save=True):
|
||||||
# Clear the image dimensions cache
|
# Clear the image dimensions cache
|
||||||
if hasattr(self, "_dimensions_cache"):
|
if hasattr(self, "_dimensions_cache"):
|
||||||
|
@ -437,6 +437,11 @@ Miscellaneous
|
|||||||
:class:`~django.core.exceptions.FieldError` when saving a file without a
|
:class:`~django.core.exceptions.FieldError` when saving a file without a
|
||||||
``name``.
|
``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:
|
.. _deprecated-features-5.1:
|
||||||
|
|
||||||
Features deprecated in 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.functional import SimpleLazyObject
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from .storage import NoReadFileSystemStorage
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -373,6 +375,21 @@ if Image:
|
|||||||
width_field="headshot_width",
|
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):
|
class CustomJSONDecoder(json.JSONDecoder):
|
||||||
def __init__(self, object_hook=None, *args, **kwargs):
|
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 (
|
from .models import (
|
||||||
Person,
|
Person,
|
||||||
PersonDimensionsFirst,
|
PersonDimensionsFirst,
|
||||||
|
PersonNoReadImage,
|
||||||
PersonTwoImages,
|
PersonTwoImages,
|
||||||
PersonWithHeight,
|
PersonWithHeight,
|
||||||
PersonWithHeightAndWidth,
|
PersonWithHeightAndWidth,
|
||||||
@ -30,7 +31,7 @@ else:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
|
PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
|
||||||
PersonTwoImages = Person
|
PersonTwoImages = PersonNoReadImage = Person
|
||||||
|
|
||||||
|
|
||||||
class ImageFieldTestMixin(SerializeMixin):
|
class ImageFieldTestMixin(SerializeMixin):
|
||||||
@ -469,3 +470,28 @@ class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
|
|||||||
# Dimensions were recalculated, and hence file should have opened.
|
# Dimensions were recalculated, and hence file should have opened.
|
||||||
self.assertIs(p.mugshot.was_opened, True)
|
self.assertIs(p.mugshot.was_opened, True)
|
||||||
self.assertIs(p.headshot.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