From a0c07d77fc313388c72a17cd59411265069f037f Mon Sep 17 00:00:00 2001 From: Manatsawin Hanmongkolchai Date: Sun, 28 May 2017 14:05:21 +0700 Subject: [PATCH] Fixed #28242 -- Moved ImageField file extension validation to the form field. --- django/db/models/fields/files.py | 2 -- django/forms/fields.py | 1 + docs/releases/1.11.2.txt | 4 ++++ docs/releases/1.11.txt | 1 + .../field_tests/test_imagefield.py | 11 +++++++++- tests/model_fields/test_imagefield.py | 8 +------- tests/model_forms/models.py | 11 ++++++++++ tests/model_forms/tests.py | 20 ++++++++++++++++++- 8 files changed, 47 insertions(+), 11 deletions(-) diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index cbd03864354..d14da97cced 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -6,7 +6,6 @@ from django.core import checks from django.core.files.base import File from django.core.files.images import ImageFile 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.fields import Field from django.utils.translation import gettext_lazy as _ @@ -355,7 +354,6 @@ class ImageFieldFile(ImageFile, FieldFile): class ImageField(FileField): - default_validators = [validate_image_file_extension] attr_class = ImageFieldFile descriptor_class = ImageFileDescriptor description = _("Image") diff --git a/django/forms/fields.py b/django/forms/fields.py index a4132d8f33c..d6656067458 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -591,6 +591,7 @@ class FileField(Field): class ImageField(FileField): + default_validators = [validators.validate_image_file_extension] default_error_messages = { 'invalid_image': _( "Upload a valid image. The file you uploaded was either not an " diff --git a/docs/releases/1.11.2.txt b/docs/releases/1.11.2.txt index e595d796ee4..ed7753a74fb 100644 --- a/docs/releases/1.11.2.txt +++ b/docs/releases/1.11.2.txt @@ -58,3 +58,7 @@ Bugfixes * Fixed a regression where ``file_move_safe()`` crashed when moving files to a 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`). diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 05c925259f1..a6955d14604 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -327,6 +327,7 @@ Models * :class:`~django.db.models.ImageField` now has a default :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 :class:`~django.db.models.functions.datetime.Trunc` functions. diff --git a/tests/forms_tests/field_tests/test_imagefield.py b/tests/forms_tests/field_tests/test_imagefield.py index 6fb4bed03cd..0a0f60440e6 100644 --- a/tests/forms_tests/field_tests/test_imagefield.py +++ b/tests/forms_tests/field_tests/test_imagefield.py @@ -2,7 +2,7 @@ import os import unittest from django.core.files.uploadedfile import SimpleUploadedFile -from django.forms import ImageField +from django.forms import ImageField, ValidationError from django.test import SimpleTestCase try: @@ -55,3 +55,12 @@ class ImageFieldTest(SimpleTestCase): self.assertIsNone(uploaded_file.content_type) finally: 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) diff --git a/tests/model_fields/test_imagefield.py b/tests/model_fields/test_imagefield.py index 3a86d3daf57..50af786f5a0 100644 --- a/tests/model_fields/test_imagefield.py +++ b/tests/model_fields/test_imagefield.py @@ -2,7 +2,7 @@ import os import shutil 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.images import ImageFile from django.test import TestCase @@ -130,12 +130,6 @@ class ImageFieldTests(ImageFieldTestMixin, TestCase): self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot)) 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): """ If the underlying file is unavailable, still create instantiate the diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py index 6e08eaec715..aecbeb18f39 100644 --- a/tests/model_forms/models.py +++ b/tests/model_forms/models.py @@ -192,6 +192,17 @@ try: def __str__(self): 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: test_images = False diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 6520e54d06f..e636d212d8a 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -32,7 +32,7 @@ from .models import ( ) if test_images: - from .models import ImageFile, OptionalImageFile + from .models import ImageFile, OptionalImageFile, NoExtensionImageFile class ImageFileForm(forms.ModelForm): class Meta: @@ -44,6 +44,11 @@ if test_images: model = OptionalImageFile fields = '__all__' + class NoExtensionImageFileForm(forms.ModelForm): + class Meta: + model = NoExtensionImageFile + fields = '__all__' + class ProductForm(forms.ModelForm): class Meta: @@ -2461,6 +2466,19 @@ class FileAndImageFieldTests(TestCase): self.assertEqual(instance.image.name, 'foo/test4.png') 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): def test_big_integer_field(self):