diff --git a/django/core/files/storage.py b/django/core/files/storage.py index cbdab1de47..4c27fce605 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -60,6 +60,14 @@ class Storage: """ return get_valid_filename(name) + def get_alternative_name(self, file_root, file_ext): + """ + Return an alternative filename, by adding an underscore and a random 7 + character alphanumeric string (before the file extension, if one + exists) to the filename. + """ + return '%s_%s%s' % (file_root, get_random_string(7), file_ext) + def get_available_name(self, name, max_length=None): """ Return a filename that's free on the target storage system and @@ -67,14 +75,13 @@ class Storage: """ dir_name, file_name = os.path.split(name) file_root, file_ext = os.path.splitext(file_name) - # 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. + # If the filename already exists, generate an alternative filename + # until it doesn't exist. # 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)) + name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) if max_length is None: continue # Truncate file_root if max_length exceeded. @@ -88,7 +95,7 @@ class Storage: '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)) + name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) return name def generate_filename(self, filename): diff --git a/docs/howto/custom-file-storage.txt b/docs/howto/custom-file-storage.txt index 0ad094da79..d9e18a6003 100644 --- a/docs/howto/custom-file-storage.txt +++ b/docs/howto/custom-file-storage.txt @@ -97,6 +97,12 @@ non-standard characters are converted to safe filenames. The code provided on ``Storage`` retains only alpha-numeric characters, periods and underscores from the original filename, removing everything else. +.. method:: get_alternative_name(file_root, file_ext) + +Returns an alternative filename based on the ``file_root`` and ``file_ext`` +parameters. By default, an underscore plus a random 7 character alphanumeric +string is appended to the filename before the extension. + .. method:: get_available_name(name, max_length=None) Returns a filename that is available in the storage mechanism, possibly taking @@ -108,5 +114,5 @@ The length of the filename will not exceed ``max_length``, if provided. If a free unique filename cannot be found, a :exc:`SuspiciousFileOperation ` exception is raised. -If a file with ``name`` already exists, an underscore plus a random 7 character -alphanumeric string is appended to the filename before the extension. +If a file with ``name`` already exists, ``get_alternative_name()`` is called to +obtain an alternative name. diff --git a/docs/ref/files/storage.txt b/docs/ref/files/storage.txt index 2a82402fac..0724a0cda1 100644 --- a/docs/ref/files/storage.txt +++ b/docs/ref/files/storage.txt @@ -105,6 +105,12 @@ The ``Storage`` class If :setting:`USE_TZ` is ``True``, returns an aware ``datetime``, otherwise returns a naive ``datetime`` in the local timezone. + .. method:: get_alternative_name(file_root, file_ext) + + Returns an alternative filename based on the ``file_root`` and + ``file_ext`` parameters, an underscore plus a random 7 character + alphanumeric string is appended to the filename before the extension. + .. method:: get_available_name(name, max_length=None) Returns a filename based on the ``name`` parameter that's free and @@ -116,9 +122,8 @@ The ``Storage`` class :exc:`SuspiciousFileOperation ` exception will be raised. - If a file with ``name`` already exists, an underscore plus a random - 7 character alphanumeric string is appended to the filename before - the extension. + If a file with ``name`` already exists, :meth:`get_alternative_name` is + called to obtain an alternative name. .. method:: get_created_time(name) diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index 7a9adb3b39..910d3a2592 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -227,7 +227,9 @@ Email File Storage ~~~~~~~~~~~~ -* ... +* The new :meth:`.Storage.get_alternative_name` method allows customizing the + algorithm for generating filenames if a file with the uploaded name already + exists. File Uploads ~~~~~~~~~~~~