diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index bf30a78..3fb2a2f 100644
a
|
b
|
|
1 | 1 | import os |
2 | 2 | import errno |
3 | 3 | import urlparse |
| 4 | import itertools |
4 | 5 | |
5 | 6 | from django.conf import settings |
6 | 7 | from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation |
… |
… |
class Storage(object):
|
63 | 64 | Returns a filename that's free on the target storage system, and |
64 | 65 | available for new content to be written to. |
65 | 66 | """ |
66 | | # If the filename already exists, keep adding an underscore to the name |
67 | | # of the file until the filename doesn't exist. |
68 | | while self.exists(name): |
| 67 | # If the filename already exists, add a number to the filename |
| 68 | # until an available name is found. |
| 69 | if self.exists(name): |
| 70 | # Build a filename template out of the (existing) name |
69 | 71 | try: |
70 | 72 | dot_index = name.rindex('.') |
71 | 73 | except ValueError: # filename has no dot |
72 | | name += '_' |
| 74 | template = "%s_%%d" % (name,) |
73 | 75 | else: |
74 | | name = name[:dot_index] + '_' + name[dot_index:] |
| 76 | template = "%s_%%d%s" % (name[:dot_index], name[dot_index:],) |
| 77 | # Iterate indefinitely until a filename is found. |
| 78 | count = itertools.count(1) |
| 79 | while self.exists(name): |
| 80 | name = template % count.next() |
75 | 81 | return name |
76 | 82 | |
77 | 83 | def path(self, name): |
diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py
index ba3eb99..182eab7 100644
a
|
b
|
ValueError: The 'normal' attribute has no file associated with it.
|
93 | 93 | >>> obj2 = Storage() |
94 | 94 | >>> obj2.normal.save('django_test.txt', ContentFile('more content')) |
95 | 95 | >>> obj2.normal |
96 | | <FieldFile: tests/django_test_.txt> |
| 96 | <FieldFile: tests/django_test_1.txt> |
97 | 97 | >>> obj2.normal.size |
98 | 98 | 12 |
99 | 99 | |
… |
… |
ValueError: The 'normal' attribute has no file associated with it.
|
102 | 102 | >>> cache.set('obj1', obj1) |
103 | 103 | >>> cache.set('obj2', obj2) |
104 | 104 | >>> cache.get('obj2').normal |
105 | | <FieldFile: tests/django_test_.txt> |
| 105 | <FieldFile: tests/django_test_1.txt> |
106 | 106 | |
107 | 107 | # Deleting an object deletes the file it uses, if there are no other objects |
108 | 108 | # still using that file. |
… |
… |
ValueError: The 'normal' attribute has no file associated with it.
|
110 | 110 | >>> obj2.delete() |
111 | 111 | >>> obj2.normal.save('django_test.txt', ContentFile('more content')) |
112 | 112 | >>> obj2.normal |
113 | | <FieldFile: tests/django_test_.txt> |
| 113 | <FieldFile: tests/django_test_1.txt> |
114 | 114 | |
115 | 115 | # Default values allow an object to access a single file. |
116 | 116 | |
diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py
index 6b219f0..bb1f3eb 100644
a
|
b
|
class FileSaveRaceConditionTest(TestCase):
|
121 | 121 | name = self.save_file('conflict') |
122 | 122 | self.thread.join() |
123 | 123 | self.assert_(self.storage.exists('conflict')) |
124 | | self.assert_(self.storage.exists('conflict_')) |
| 124 | self.assert_(self.storage.exists('conflict_1')) |
125 | 125 | self.storage.delete('conflict') |
126 | | self.storage.delete('conflict_') |
| 126 | self.storage.delete('conflict_1') |
127 | 127 | |
128 | 128 | class FileStoragePermissions(TestCase): |
129 | 129 | def setUp(self): |