diff options
author | Pavel Shpilev <pavel.shpilev@medibank.com.au> | 2014-10-15 18:42:06 +1100 |
---|---|---|
committer | Tim Graham <timograham@gmail.com> | 2015-01-12 09:09:18 -0500 |
commit | a7c256cb5491bf2a77abdff01638239db5bfd9d5 (patch) | |
tree | 3b3cd98b0f2f42efd989ae9dd9ab3dba473d972a /django | |
parent | b5c1a85b50c709770b8e98aeecfeb8e81ca29dcf (diff) | |
download | django-a7c256cb5491bf2a77abdff01638239db5bfd9d5.tar.gz |
Fixed #9893 -- Allowed using a field's max_length in the Storage.
Diffstat (limited to 'django')
-rw-r--r-- | django/core/files/storage.py | 44 | ||||
-rw-r--r-- | django/db/models/fields/files.py | 17 |
2 files changed, 53 insertions, 8 deletions
diff --git a/django/core/files/storage.py b/django/core/files/storage.py index 2d7c0c78e9..90ab1be27a 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -1,8 +1,11 @@ -import os -import errno from datetime import datetime +import errno +from inspect import getargspec +import os +import warnings from django.conf import settings +from django.core.exceptions import SuspiciousFileOperation from django.core.files import locks, File from django.core.files.move import file_move_safe from django.utils.crypto import get_random_string @@ -13,6 +16,7 @@ from django.utils.six.moves.urllib.parse import urljoin from django.utils.text import get_valid_filename from django.utils._os import safe_join, abspathu from django.utils.deconstruct import deconstructible +from django.utils.deprecation import RemovedInDjango20Warning __all__ = ('Storage', 'FileSystemStorage', 'DefaultStorage', 'default_storage') @@ -33,7 +37,7 @@ class Storage(object): """ return self._open(name, mode) - def save(self, name, content): + def save(self, name, content, max_length=None): """ Saves new content to the file specified by name. The content should be a proper File object or any python file-like object, ready to be read @@ -46,7 +50,18 @@ class Storage(object): if not hasattr(content, 'chunks'): content = File(content) - name = self.get_available_name(name) + args, varargs, varkw, defaults = getargspec(self.get_available_name) + if 'max_length' in args: + name = self.get_available_name(name, max_length=max_length) + else: + warnings.warn( + 'Backwards compatibility for storage backends without ' + 'support for the `max_length` argument in ' + 'Storage.get_available_name() will be removed in Django 2.0.', + RemovedInDjango20Warning, stacklevel=2 + ) + name = self.get_available_name(name) + name = self._save(name, content) # Store filenames with forward slashes, even on Windows @@ -61,7 +76,7 @@ class Storage(object): """ return get_valid_filename(name) - def get_available_name(self, name): + def get_available_name(self, name, max_length=None): """ Returns a filename that's free on the target storage system, and available for new content to be written to. @@ -71,10 +86,25 @@ class Storage(object): # If the filename already exists, add an underscore and a random 7 # character alphanumeric string (before the file extension, if one # exists) to the filename until the generated filename doesn't exist. - while self.exists(name): + # Truncate original name if required, so the new filename does not + # exceed the max_length. + while self.exists(name) or (max_length and len(name) > max_length): # file_ext includes the dot. name = os.path.join(dir_name, "%s_%s%s" % (file_root, get_random_string(7), file_ext)) - + if max_length is None: + continue + # Truncate file_root if max_length exceeded. + truncation = len(name) - max_length + if truncation > 0: + file_root = file_root[:-truncation] + # Entire file_root was truncated in attempt to find an available filename. + if not file_root: + raise SuspiciousFileOperation( + 'Storage can not find an available filename for "%s". ' + 'Please make sure that the corresponding file field ' + 'allows sufficient "max_length".' % name + ) + name = os.path.join(dir_name, "%s_%s%s" % (file_root, get_random_string(7), file_ext)) return name def path(self, name): diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index 8ca8532113..301244bbef 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -1,5 +1,7 @@ import datetime +from inspect import getargspec import os +import warnings from django import forms from django.db.models.fields import Field @@ -11,6 +13,7 @@ from django.db.models import signals from django.utils.encoding import force_str, force_text from django.utils import six from django.utils.translation import ugettext_lazy as _ +from django.utils.deprecation import RemovedInDjango20Warning class FieldFile(File): @@ -85,7 +88,19 @@ class FieldFile(File): def save(self, name, content, save=True): name = self.field.generate_filename(self.instance, name) - self.name = self.storage.save(name, content) + + args, varargs, varkw, defaults = getargspec(self.storage.save) + if 'max_length' in args: + self.name = self.storage.save(name, content, max_length=self.field.max_length) + else: + warnings.warn( + 'Backwards compatibility for storage backends without ' + 'support for the `max_length` argument in ' + 'Storage.save() will be removed in Django 2.0.', + RemovedInDjango20Warning, stacklevel=2 + ) + self.name = self.storage.save(name, content) + setattr(self.instance, self.field.name, self.name) # Update the filesize cache |