Skip to content

Commit 110bd82

Browse files
whstimgraham
authored andcommitted
[1.11.x] Fixed #28242 -- Moved ImageField file extension validation to the form field.
Backport of a0c07d7 from master
1 parent af9a81a commit 110bd82

File tree

8 files changed

+47
-11
lines changed

8 files changed

+47
-11
lines changed

django/db/models/fields/files.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from django.core.files.base import File
99
from django.core.files.images import ImageFile
1010
from django.core.files.storage import default_storage
11-
from django.core.validators import validate_image_file_extension
1211
from django.db.models import signals
1312
from django.db.models.fields import Field
1413
from django.utils import six
@@ -387,7 +386,6 @@ def delete(self, save=True):
387386

388387

389388
class ImageField(FileField):
390-
default_validators = [validate_image_file_extension]
391389
attr_class = ImageFieldFile
392390
descriptor_class = ImageFileDescriptor
393391
description = _("Image")

django/forms/fields.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ def has_changed(self, initial, data):
610610

611611

612612
class ImageField(FileField):
613+
default_validators = [validators.validate_image_file_extension]
613614
default_error_messages = {
614615
'invalid_image': _(
615616
"Upload a valid image. The file you uploaded was either not an "

docs/releases/1.11.2.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,7 @@ Bugfixes
5858

5959
* Fixed a regression where ``file_move_safe()`` crashed when moving files to a
6060
CIFS mount (:ticket:`28170`).
61+
62+
* Moved the ``ImageField`` file extension validation added in Django 1.11 from
63+
the model field to the form field to reallow the use case of storing images
64+
without an extension (:ticket:`28242`).

docs/releases/1.11.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ Models
327327

328328
* :class:`~django.db.models.ImageField` now has a default
329329
:data:`~django.core.validators.validate_image_file_extension` validator.
330+
(This validator moved to the form field in :doc:`Django 1.11.2 <1.11.2>`.)
330331

331332
* Added support for time truncation to
332333
:class:`~django.db.models.functions.datetime.Trunc` functions.

tests/forms_tests/field_tests/test_imagefield.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import unittest
55

66
from django.core.files.uploadedfile import SimpleUploadedFile
7-
from django.forms import ImageField
7+
from django.forms import ImageField, ValidationError
88
from django.test import SimpleTestCase
99
from django.utils._os import upath
1010

@@ -58,3 +58,12 @@ def test_imagefield_annotate_with_bitmap_image_after_clean(self):
5858
self.assertIsNone(uploaded_file.content_type)
5959
finally:
6060
Image.register_mime(BmpImageFile.format, 'image/bmp')
61+
62+
def test_file_extension_validation(self):
63+
f = ImageField()
64+
img_path = get_img_path('filepath_test_files/1x1.png')
65+
with open(img_path, 'rb') as img_file:
66+
img_data = img_file.read()
67+
img_file = SimpleUploadedFile('1x1.txt', img_data)
68+
with self.assertRaisesMessage(ValidationError, "File extension 'txt' is not allowed."):
69+
f.clean(img_file)

tests/model_fields/test_imagefield.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import shutil
55
from unittest import skipIf
66

7-
from django.core.exceptions import ImproperlyConfigured, ValidationError
7+
from django.core.exceptions import ImproperlyConfigured
88
from django.core.files import File
99
from django.core.files.images import ImageFile
1010
from django.test import TestCase
@@ -133,12 +133,6 @@ def test_equal_notequal_hash(self):
133133
self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot))
134134
self.assertIs(p1_db.mugshot != p1.mugshot, False)
135135

136-
def test_validation(self):
137-
p = self.PersonModel(name="Joan")
138-
p.mugshot.save("shot.txt", self.file1)
139-
with self.assertRaisesMessage(ValidationError, "File extension 'txt' is not allowed."):
140-
p.full_clean()
141-
142136
def test_instantiate_missing(self):
143137
"""
144138
If the underlying file is unavailable, still create instantiate the

tests/model_forms/models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,17 @@ def custom_upload_path(self, filename):
214214

215215
def __str__(self):
216216
return self.description
217+
218+
class NoExtensionImageFile(models.Model):
219+
def upload_to(self, filename):
220+
return 'tests/no_extension'
221+
222+
description = models.CharField(max_length=20)
223+
image = models.ImageField(storage=temp_storage, upload_to=upload_to)
224+
225+
def __str__(self):
226+
return self.description
227+
217228
except ImportError:
218229
test_images = False
219230

tests/model_forms/tests.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
)
3737

3838
if test_images:
39-
from .models import ImageFile, OptionalImageFile
39+
from .models import ImageFile, OptionalImageFile, NoExtensionImageFile
4040

4141
class ImageFileForm(forms.ModelForm):
4242
class Meta:
@@ -48,6 +48,11 @@ class Meta:
4848
model = OptionalImageFile
4949
fields = '__all__'
5050

51+
class NoExtensionImageFileForm(forms.ModelForm):
52+
class Meta:
53+
model = NoExtensionImageFile
54+
fields = '__all__'
55+
5156

5257
class ProductForm(forms.ModelForm):
5358
class Meta:
@@ -2469,6 +2474,19 @@ def test_image_field(self):
24692474
self.assertEqual(instance.image.name, 'foo/test4.png')
24702475
instance.delete()
24712476

2477+
# Editing an instance that has an image without an extension shouldn't
2478+
# fail validation. First create:
2479+
f = NoExtensionImageFileForm(
2480+
data={'description': 'An image'},
2481+
files={'image': SimpleUploadedFile('test.png', image_data)},
2482+
)
2483+
self.assertTrue(f.is_valid())
2484+
instance = f.save()
2485+
self.assertEqual(instance.image.name, 'tests/no_extension')
2486+
# Then edit:
2487+
f = NoExtensionImageFileForm(data={'description': 'Edited image'}, instance=instance)
2488+
self.assertTrue(f.is_valid())
2489+
24722490

24732491
class ModelOtherFieldTests(SimpleTestCase):
24742492
def test_big_integer_field(self):

0 commit comments

Comments
 (0)