From dcd4383107d96c18bcb53312ca4de070374b334c Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 May 2012 16:02:05 +0200 Subject: [PATCH] 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. --- django/db/models/fields/files.py | 21 +++++++++++++++++++++ tests/regressiontests/model_fields/tests.py | 12 ++++++++++++ 2 files changed, 33 insertions(+) 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()