Author: jacob
Date: 2010-02-23 16:39:22 -0600 (Tue, 23 Feb 2010)
New Revision: 12552

Modified:
   django/trunk/django/core/files/storage.py
   django/trunk/docs/howto/custom-file-storage.txt
   django/trunk/tests/modeltests/files/models.py
   django/trunk/tests/regressiontests/file_storage/tests.py
Log:
Fixed #10258: handle duplicate file names better.

Instead of just continually appending "_" to duplicate file names, Django's
default storage now appends `_1`, `_2`, `_3`, etc.

Thanks to ianschenck and Thilo.

Modified: django/trunk/django/core/files/storage.py
===================================================================
--- django/trunk/django/core/files/storage.py   2010-02-23 21:58:58 UTC (rev 
12551)
+++ django/trunk/django/core/files/storage.py   2010-02-23 22:39:22 UTC (rev 
12552)
@@ -1,6 +1,7 @@
 import os
 import errno
 import urlparse
+import itertools
 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
@@ -65,13 +66,14 @@
         """
         dir_name, file_name = os.path.split(name)
         file_root, file_ext = os.path.splitext(file_name)
-        # If the filename already exists, keep adding an underscore (before the
-        # file extension, if one exists) to the filename until the generated
+        # If the filename already exists, add an underscore and a number 
(before
+        # the file extension, if one exists) to the filename until the 
generated
         # filename doesn't exist.
+        count = itertools.count(1)
         while self.exists(name):
-            file_root += '_'
             # file_ext includes the dot.
-            name = os.path.join(dir_name, file_root + file_ext)
+            name = os.path.join(dir_name, "%s_%s%s" % (file_root, 
count.next(), file_ext))
+
         return name
 
     def path(self, name):

Modified: django/trunk/docs/howto/custom-file-storage.txt
===================================================================
--- django/trunk/docs/howto/custom-file-storage.txt     2010-02-23 21:58:58 UTC 
(rev 12551)
+++ django/trunk/docs/howto/custom-file-storage.txt     2010-02-23 22:39:22 UTC 
(rev 12552)
@@ -88,5 +88,5 @@
 will have already cleaned to a filename valid for the storage system, according
 to the ``get_valid_name()`` method described above.
 
-The code provided on ``Storage`` simply appends underscores to the filename
-until it finds one that's available in the destination directory.
+The code provided on ``Storage`` simply appends ``"_1"``, ``"_2"``, etc. to the
+filename until it finds one that's available in the destination directory.

Modified: django/trunk/tests/modeltests/files/models.py
===================================================================
--- django/trunk/tests/modeltests/files/models.py       2010-02-23 21:58:58 UTC 
(rev 12551)
+++ django/trunk/tests/modeltests/files/models.py       2010-02-23 22:39:22 UTC 
(rev 12552)
@@ -5,14 +5,11 @@
 and where files should be stored.
 """
 
-import shutil
 import random
 import tempfile
 from django.db import models
 from django.core.files.base import ContentFile
-from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.files.storage import FileSystemStorage
-from django.core.cache import cache
 
 temp_storage_location = tempfile.mkdtemp()
 temp_storage = FileSystemStorage(location=temp_storage_location)
@@ -64,6 +61,7 @@
 # File objects can be assigned to FileField attributes,  but shouldn't get
 # committed until the model it's attached to is saved.
 
+>>> from django.core.files.uploadedfile import SimpleUploadedFile
 >>> obj1.normal = SimpleUploadedFile('assignment.txt', 'content')
 >>> dirs, files = temp_storage.listdir('tests')
 >>> dirs
@@ -93,16 +91,17 @@
 >>> obj2 = Storage()
 >>> obj2.normal.save('django_test.txt', ContentFile('more content'))
 >>> obj2.normal
-<FieldFile: tests/django_test_.txt>
+<FieldFile: tests/django_test_1.txt>
 >>> obj2.normal.size
 12
 
 # Push the objects into the cache to make sure they pickle properly
 
+>>> from django.core.cache import cache
 >>> cache.set('obj1', obj1)
 >>> cache.set('obj2', obj2)
 >>> cache.get('obj2').normal
-<FieldFile: tests/django_test_.txt>
+<FieldFile: tests/django_test_1.txt>
 
 # Deleting an object deletes the file it uses, if there are no other objects
 # still using that file.
@@ -110,8 +109,18 @@
 >>> obj2.delete()
 >>> obj2.normal.save('django_test.txt', ContentFile('more content'))
 >>> obj2.normal
-<FieldFile: tests/django_test_.txt>
+<FieldFile: tests/django_test_1.txt>
 
+# Multiple files with the same name get _N appended to them.
+
+>>> objs = [Storage() for i in range(3)] 
+>>> for o in objs:  
+...     o.normal.save('multiple_files.txt', ContentFile('Same Content')) 
+>>> [o.normal for o in objs]
+[<FieldFile: tests/multiple_files.txt>, <FieldFile: 
tests/multiple_files_1.txt>, <FieldFile: tests/multiple_files_2.txt>]
+>>> for o in objs: 
+...     o.delete() 
+
 # Default values allow an object to access a single file.
 
 >>> obj3 = Storage.objects.create()
@@ -139,5 +148,7 @@
 >>> obj2.normal.delete()
 >>> obj3.default.delete()
 >>> obj4.random.delete()
+
+>>> import shutil
 >>> shutil.rmtree(temp_storage_location)
 """}

Modified: django/trunk/tests/regressiontests/file_storage/tests.py
===================================================================
--- django/trunk/tests/regressiontests/file_storage/tests.py    2010-02-23 
21:58:58 UTC (rev 12551)
+++ django/trunk/tests/regressiontests/file_storage/tests.py    2010-02-23 
22:39:22 UTC (rev 12552)
@@ -126,9 +126,9 @@
         name = self.save_file('conflict')
         self.thread.join()
         self.assert_(self.storage.exists('conflict'))
-        self.assert_(self.storage.exists('conflict_'))
+        self.assert_(self.storage.exists('conflict_1'))
         self.storage.delete('conflict')
-        self.storage.delete('conflict_')
+        self.storage.delete('conflict_1')
 
 class FileStoragePermissions(TestCase):
     def setUp(self):
@@ -167,7 +167,7 @@
 
         self.failIf(os.path.exists(os.path.join(self.storage_dir, 
'dotted_.path')))
         self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/test')))
-        self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/test_')))
+        self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/test_1')))
 
     def test_first_character_dot(self):
         """
@@ -183,7 +183,7 @@
         if sys.version_info < (2, 6):
             self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/_.test')))
         else:
-            self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/.test_')))
+            self.assert_(os.path.exists(os.path.join(self.storage_dir, 
'dotted.path/.test_1')))
 
 if Image is not None:
     class DimensionClosingBug(TestCase):

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-upda...@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to