Browse Source

Refs #34517 -- Restored skipping ImageFileField.update_dimension_fields without width/height fields.

This avoids reading the image size when the dimensions fields
(image_width, image_height) do not exist, as that operation may be
expensive.

Partially reverts ea53e7c09f1b8864c20c65976bbeaeab77abdaec, that dropped
the check for the dimension fields in update_dimension_fields(), because
the post_init signal was no longer registered without dimension fields.

However, another code path to that function exists: when the
ImageFileField is save()d, the name from the storage is setattr()ed on
the field, and ImageFileDescriptor calls update_dimension_fields()
because the image size might have changed. Keep bailing out early when
dimensions are unused.

Besides, computing the image dimensions causes to close() the file,
resulting in a backward-incompatible change. The test protects against
that change.
François Freitag 1 year ago
parent
commit
bcacc6321a
2 changed files with 11 additions and 2 deletions
  1. 4 2
      django/db/models/fields/files.py
  2. 7 0
      tests/model_fields/test_imagefield.py

+ 4 - 2
django/db/models/fields/files.py

@@ -458,8 +458,10 @@ class ImageField(FileField):
         Dimensions can be forced to update with force=True, which is how
         ImageFileDescriptor.__set__ calls this method.
         """
-        # Nothing to update if the field is deferred.
-        if self.attname not in instance.__dict__:
+        # Nothing to update if the field doesn't have dimension fields or if
+        # the field is deferred.
+        has_dimension_fields = self.width_field or self.height_field
+        if not has_dimension_fields or self.attname not in instance.__dict__:
             return
 
         # getattr will call the ImageFileDescriptor's __get__ method, which

+ 7 - 0
tests/model_fields/test_imagefield.py

@@ -336,6 +336,13 @@ class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
             [sender_id for (_, sender_id), *_ in signals.post_init.receivers],
         )
 
+    def test_save_does_not_close_file(self):
+        p = self.PersonModel(name="Joe")
+        p.mugshot.save("mug", self.file1)
+        with p.mugshot as f:
+            # Underlying file object wasn’t closed.
+            self.assertEqual(f.tell(), 0)
+
 
 @skipIf(Image is None, "Pillow is required to test ImageField")
 class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):