summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorPavel Shpilev <pavel.shpilev@medibank.com.au>2014-10-15 18:42:06 +1100
committerTim Graham <timograham@gmail.com>2015-01-12 09:09:18 -0500
commita7c256cb5491bf2a77abdff01638239db5bfd9d5 (patch)
tree3b3cd98b0f2f42efd989ae9dd9ab3dba473d972a /django
parentb5c1a85b50c709770b8e98aeecfeb8e81ca29dcf (diff)
downloaddjango-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.py44
-rw-r--r--django/db/models/fields/files.py17
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