diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index e0c095974dc..b0dce381a61 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -242,7 +242,11 @@ class FileField(Field): # (ie. upload_to='path/to/upload/dir'), the length of the generated # name equals the length of the uploaded name plus a constant. Thus # we can tell the user how much shorter the name should be (roughly). - length = len(self.generate_filename(model_instance, value.name)) + if value and value._committed: + filename = value.name + else: + filename = self.generate_filename(model_instance, value.name) + length = len(filename) if self.max_length and length > self.max_length: error_values = {'extra': length - self.max_length} raise ValidationError(self.error_messages['max_length'] % error_values) diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py index 41344727485..cefc7c7244a 100644 --- a/tests/modeltests/files/models.py +++ b/tests/modeltests/files/models.py @@ -26,5 +26,5 @@ class Storage(models.Model): normal = models.FileField(storage=temp_storage, upload_to='tests') custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to) - random = models.FileField(storage=temp_storage, upload_to=random_upload_to) + random = models.FileField(storage=temp_storage, upload_to=random_upload_to, max_length=16) default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py index 3e256f787f4..565d4c89423 100644 --- a/tests/modeltests/files/tests.py +++ b/tests/modeltests/files/tests.py @@ -5,6 +5,7 @@ import shutil import tempfile from django.core.cache import cache +from django.core.exceptions import ValidationError from django.core.files import File from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile @@ -102,11 +103,23 @@ class FileStorageTests(TestCase): obj4.random.save("random_file", ContentFile(b"random content")) self.assertTrue(obj4.random.name.endswith("/random_file")) - # Clean up the temporary files and dir. - obj1.normal.delete() - obj2.normal.delete() - obj3.default.delete() - obj4.random.delete() + def test_max_length(self): + """ + Test that FileField validates the length of the generated file name + that will be stored in the database. Regression for #9893. + """ + # upload_to = 'unused', so file names are saved as '456/xxxxx'. + # max_length = 16, so names longer than 12 characters are rejected. + s1 = Storage(random=SimpleUploadedFile(12 * 'x', b"content")) + s1.full_clean() + with self.assertRaises(ValidationError): + Storage(random=SimpleUploadedFile(13 * 'x', b"content")).full_clean() + + # Ticket #18515: validation for an already saved file should not check + # against a regenerated file name (and potentially raise a ValidationError + # if max_length is exceeded + s1.save() + s1.full_clean() class FileTests(unittest.TestCase): diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index 5d3d42ef2a5..a89ffcae320 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -365,15 +365,3 @@ class FileFieldTests(unittest.TestCase): field = d._meta.get_field('myfile') field.save_form_data(d, 'else.txt') self.assertEqual(d.myfile, 'else.txt') - - def test_max_length(self): - """ - Test that FileField validates the length of the generated file name - that will be stored in the database. Regression for #9893. - - """ - # upload_to = 'unused', so file names are saved as 'unused/xxxxx'. - # max_length = 100, so names longer than 93 characters are rejected. - Document(myfile=93 * 'x').full_clean() - with self.assertRaises(ValidationError): - Document(myfile=94 * 'x').full_clean()