Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #9893 -- Validated the length of file names

after the full file name is generated by the storage class.

Thanks Refefer for the report, carsongee for the patch, and
everyone else involved in the discussion.
  • Loading branch information...
commit dcd4383107d96c18bcb53312ca4de070374b334c 1 parent 82a76ef
@aaugustin aaugustin authored
View
21 django/db/models/fields/files.py
@@ -3,6 +3,7 @@
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 @@ def __set__(self, instance, value):
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 @@ def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **k
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"
View
12 tests/regressiontests/model_fields/tests.py
@@ -365,3 +365,15 @@ def test_changed(self):
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()

0 comments on commit dcd4383

Please sign in to comment.
Something went wrong with that request. Please try again.