diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index 6f1f183d4f..34cf84dbc0 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -3,6 +3,7 @@ import os from django import forms from django.db.models.fields import Field +from django.core.exceptions import ValidationError from django.core.files.base import File from django.core.files.storage import default_storage from django.core.files.images import ImageFile @@ -204,6 +205,11 @@ class FileDescriptor(object): instance.__dict__[self.field.name] = value class FileField(Field): + + default_error_messages = { + 'max_length': _(u'Filename is %(extra)d characters too long.') + } + # The class to wrap instance attributes in. Accessing the file object off # the instance will always return an instance of attr_class. attr_class = FieldFile @@ -226,6 +232,21 @@ class FileField(Field): kwargs['max_length'] = kwargs.get('max_length', 100) super(FileField, self).__init__(verbose_name, name, **kwargs) + def validate(self, value, model_instance): + """ + Validates that the generated file name still fits within max_length. + """ + # The generated file name stored in the database is generally longer + # than the uploaded file name. Using the length of generated name in + # the error message would be confusing. However, in the common case + # (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 self.max_length and length > self.max_length: + error_values = {'extra': length - self.max_length} + raise ValidationError(self.error_messages['max_length'] % error_values) + def get_internal_type(self): return "FileField" diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index ea1e1c7d99..4b48616e99 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -365,3 +365,15 @@ 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()