[1.11.x] Fixed #28242 -- Moved ImageField file extension validation to the form field.

Backport of a0c07d77fc from master
This commit is contained in:
Manatsawin Hanmongkolchai 2017-05-28 14:05:21 +07:00 committed by Tim Graham
parent af9a81aa7f
commit 110bd82038
8 changed files with 47 additions and 11 deletions

View File

@ -8,7 +8,6 @@ from django.core import checks
from django.core.files.base import File from django.core.files.base import File
from django.core.files.images import ImageFile from django.core.files.images import ImageFile
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.core.validators import validate_image_file_extension
from django.db.models import signals from django.db.models import signals
from django.db.models.fields import Field from django.db.models.fields import Field
from django.utils import six from django.utils import six
@ -387,7 +386,6 @@ class ImageFieldFile(ImageFile, FieldFile):
class ImageField(FileField): class ImageField(FileField):
default_validators = [validate_image_file_extension]
attr_class = ImageFieldFile attr_class = ImageFieldFile
descriptor_class = ImageFileDescriptor descriptor_class = ImageFileDescriptor
description = _("Image") description = _("Image")

View File

@ -610,6 +610,7 @@ class FileField(Field):
class ImageField(FileField): class ImageField(FileField):
default_validators = [validators.validate_image_file_extension]
default_error_messages = { default_error_messages = {
'invalid_image': _( 'invalid_image': _(
"Upload a valid image. The file you uploaded was either not an " "Upload a valid image. The file you uploaded was either not an "

View File

@ -58,3 +58,7 @@ Bugfixes
* Fixed a regression where ``file_move_safe()`` crashed when moving files to a * Fixed a regression where ``file_move_safe()`` crashed when moving files to a
CIFS mount (:ticket:`28170`). CIFS mount (:ticket:`28170`).
* Moved the ``ImageField`` file extension validation added in Django 1.11 from
the model field to the form field to reallow the use case of storing images
without an extension (:ticket:`28242`).

View File

@ -327,6 +327,7 @@ Models
* :class:`~django.db.models.ImageField` now has a default * :class:`~django.db.models.ImageField` now has a default
:data:`~django.core.validators.validate_image_file_extension` validator. :data:`~django.core.validators.validate_image_file_extension` validator.
(This validator moved to the form field in :doc:`Django 1.11.2 <1.11.2>`.)
* Added support for time truncation to * Added support for time truncation to
:class:`~django.db.models.functions.datetime.Trunc` functions. :class:`~django.db.models.functions.datetime.Trunc` functions.

View File

@ -4,7 +4,7 @@ import os
import unittest import unittest
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import ImageField from django.forms import ImageField, ValidationError
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.utils._os import upath from django.utils._os import upath
@ -58,3 +58,12 @@ class ImageFieldTest(SimpleTestCase):
self.assertIsNone(uploaded_file.content_type) self.assertIsNone(uploaded_file.content_type)
finally: finally:
Image.register_mime(BmpImageFile.format, 'image/bmp') Image.register_mime(BmpImageFile.format, 'image/bmp')
def test_file_extension_validation(self):
f = ImageField()
img_path = get_img_path('filepath_test_files/1x1.png')
with open(img_path, 'rb') as img_file:
img_data = img_file.read()
img_file = SimpleUploadedFile('1x1.txt', img_data)
with self.assertRaisesMessage(ValidationError, "File extension 'txt' is not allowed."):
f.clean(img_file)

View File

@ -4,7 +4,7 @@ import os
import shutil import shutil
from unittest import skipIf from unittest import skipIf
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import ImproperlyConfigured
from django.core.files import File from django.core.files import File
from django.core.files.images import ImageFile from django.core.files.images import ImageFile
from django.test import TestCase from django.test import TestCase
@ -133,12 +133,6 @@ class ImageFieldTests(ImageFieldTestMixin, TestCase):
self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot)) self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot))
self.assertIs(p1_db.mugshot != p1.mugshot, False) self.assertIs(p1_db.mugshot != p1.mugshot, False)
def test_validation(self):
p = self.PersonModel(name="Joan")
p.mugshot.save("shot.txt", self.file1)
with self.assertRaisesMessage(ValidationError, "File extension 'txt' is not allowed."):
p.full_clean()
def test_instantiate_missing(self): def test_instantiate_missing(self):
""" """
If the underlying file is unavailable, still create instantiate the If the underlying file is unavailable, still create instantiate the

View File

@ -214,6 +214,17 @@ try:
def __str__(self): def __str__(self):
return self.description return self.description
class NoExtensionImageFile(models.Model):
def upload_to(self, filename):
return 'tests/no_extension'
description = models.CharField(max_length=20)
image = models.ImageField(storage=temp_storage, upload_to=upload_to)
def __str__(self):
return self.description
except ImportError: except ImportError:
test_images = False test_images = False

View File

@ -36,7 +36,7 @@ from .models import (
) )
if test_images: if test_images:
from .models import ImageFile, OptionalImageFile from .models import ImageFile, OptionalImageFile, NoExtensionImageFile
class ImageFileForm(forms.ModelForm): class ImageFileForm(forms.ModelForm):
class Meta: class Meta:
@ -48,6 +48,11 @@ if test_images:
model = OptionalImageFile model = OptionalImageFile
fields = '__all__' fields = '__all__'
class NoExtensionImageFileForm(forms.ModelForm):
class Meta:
model = NoExtensionImageFile
fields = '__all__'
class ProductForm(forms.ModelForm): class ProductForm(forms.ModelForm):
class Meta: class Meta:
@ -2469,6 +2474,19 @@ class FileAndImageFieldTests(TestCase):
self.assertEqual(instance.image.name, 'foo/test4.png') self.assertEqual(instance.image.name, 'foo/test4.png')
instance.delete() instance.delete()
# Editing an instance that has an image without an extension shouldn't
# fail validation. First create:
f = NoExtensionImageFileForm(
data={'description': 'An image'},
files={'image': SimpleUploadedFile('test.png', image_data)},
)
self.assertTrue(f.is_valid())
instance = f.save()
self.assertEqual(instance.image.name, 'tests/no_extension')
# Then edit:
f = NoExtensionImageFileForm(data={'description': 'Edited image'}, instance=instance)
self.assertTrue(f.is_valid())
class ModelOtherFieldTests(SimpleTestCase): class ModelOtherFieldTests(SimpleTestCase):
def test_big_integer_field(self): def test_big_integer_field(self):