[1.11.x] Fixed #28242 -- Moved ImageField file extension validation to the form field.
Backport of a0c07d77fc
from master
This commit is contained in:
parent
af9a81aa7f
commit
110bd82038
|
@ -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")
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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`).
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue