Browse Source

Fixed #30736 -- Added Storage.get_alternative_name() to allow customization.

yukihira1992 5 years ago
parent
commit
0545781764

+ 12 - 5
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):

+ 8 - 2
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
 <django.core.exceptions.SuspiciousOperation>` 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.

+ 8 - 3
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
         <django.core.exceptions.SuspiciousOperation>` 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)
 

+ 3 - 1
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
 ~~~~~~~~~~~~