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.