Skip to content

Commit 9227427

Browse files
committed
Merge pull request #20 from pinax/duplicates
Disallow Duplicate Document and Folder Names
2 parents e413307 + 7904cad commit 9227427

File tree

5 files changed

+107
-14
lines changed

5 files changed

+107
-14
lines changed

pinax/documents/exceptions.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class DuplicateFolderNameError(Exception):
2+
pass
3+
4+
5+
class DuplicateDocumentNameError(Exception):
6+
pass

pinax/documents/forms.py

+12
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ class Meta:
2121
"parent": forms.HiddenInput,
2222
}
2323

24+
def clean(self):
25+
name = self.cleaned_data["name"]
26+
parent = self.cleaned_data.get("parent")
27+
if Folder.already_exists(name, parent):
28+
raise forms.ValidationError("{} already exists.".format(name))
29+
2430
def __init__(self, *args, **kwargs):
2531
folders = kwargs.pop("folders")
2632
super(FolderCreateForm, self).__init__(*args, **kwargs)
@@ -48,6 +54,12 @@ def clean_file(self):
4854
raise forms.ValidationError("File will exceed storage capacity.")
4955
return value
5056

57+
def clean(self):
58+
name = self.cleaned_data["file"].name
59+
folder = self.cleaned_data.get("folder")
60+
if Document.already_exists(name, folder):
61+
raise forms.ValidationError("{} already exists.".format(name))
62+
5163

5264
class UserMultipleChoiceField(forms.ModelMultipleChoiceField):
5365

pinax/documents/models.py

+13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from .compat import izip_longest
1313
from .conf import settings
14+
from .exceptions import DuplicateFolderNameError, DuplicateDocumentNameError
1415
from .hooks import hookset
1516
from .managers import FolderManager, FolderQuerySet, DocumentQuerySet
1617

@@ -40,10 +41,16 @@ class Folder(models.Model):
4041
def shared_user_model(cls):
4142
return FolderSharedUser
4243

44+
@classmethod
45+
def already_exists(cls, name, parent=None):
46+
return cls.objects.filter(name=name, parent=parent).exists()
47+
4348
def __unicode__(self):
4449
return self.name
4550

4651
def save(self, **kwargs):
52+
if not self.pk and Folder.already_exists(self.name, self.parent):
53+
raise DuplicateFolderNameError("{} already exists in this folder.".format(self.name))
4754
self.touch(self.author, commit=False)
4855
super(Folder, self).save(**kwargs)
4956

@@ -185,10 +192,16 @@ class Document(models.Model):
185192
def shared_user_model(cls):
186193
return DocumentSharedUser
187194

195+
@classmethod
196+
def already_exists(cls, name, folder=None):
197+
return cls.objects.filter(name=name, folder=folder).exists()
198+
188199
def __unicode__(self):
189200
return self.name
190201

191202
def save(self, **kwargs):
203+
if not self.pk and Document.already_exists(self.name, self.folder):
204+
raise DuplicateDocumentNameError("{} already exists in this folder.".format(self.name))
192205
self.touch(self.author, commit=False)
193206
super(Document, self).save(**kwargs)
194207

pinax/documents/tests/test.py

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,49 @@
1+
from django.core.files.uploadedfile import SimpleUploadedFile
2+
13
from test_plus.test import TestCase as PlusTestCase
24

5+
from ..exceptions import DuplicateFolderNameError, DuplicateDocumentNameError
6+
from ..models import (
7+
Document,
8+
Folder
9+
)
10+
11+
12+
class BaseTest(PlusTestCase):
13+
14+
def setUp(self):
15+
self.user = self.make_user("eldarion")
16+
17+
18+
class FolderTestCase(BaseTest):
19+
20+
def test_cannot_have_duplicate_sibling_folder_names(self):
21+
Folder.objects.create(name="Foo", author=self.user, modified_by=self.user)
22+
with self.assertRaises(DuplicateFolderNameError):
23+
Folder.objects.create(name="Foo", author=self.user, modified_by=self.user)
24+
25+
def test_can_have_duplicate_folder_names_at_different_paths(self):
26+
foo = Folder.objects.create(name="Foo", author=self.user, modified_by=self.user)
27+
bar = Folder.objects.create(name="Bar", parent=foo, author=self.user, modified_by=self.user)
28+
Folder.objects.create(name="Foo", parent=bar, author=self.user, modified_by=self.user)
29+
with self.assertRaises(DuplicateFolderNameError):
30+
Folder.objects.create(name="Bar", parent=foo, author=self.user, modified_by=self.user)
31+
32+
33+
class DocumentTestCase(BaseTest):
34+
35+
def test_cannot_have_duplicate_sibling_document_names(self):
36+
simple_file = SimpleUploadedFile("delicious.txt", b"something tasty")
37+
Document.objects.create(name="Foo", author=self.user, file=simple_file, modified_by=self.user)
38+
with self.assertRaises(DuplicateDocumentNameError):
39+
Document.objects.create(name="Foo", author=self.user, file=simple_file, modified_by=self.user)
340

4-
class TestCase(PlusTestCase):
5-
pass
41+
def test_can_have_duplicate_document_names_at_different_paths(self):
42+
simple_file = SimpleUploadedFile("delicious.txt", b"something tasty")
43+
foo = Folder.objects.create(name="Foo", author=self.user, modified_by=self.user)
44+
bar = Folder.objects.create(name="Bar", parent=foo, author=self.user, modified_by=self.user)
45+
Document.objects.create(name="Foo", author=self.user, file=simple_file, modified_by=self.user)
46+
Document.objects.create(name="Bar", folder=foo, author=self.user, file=simple_file, modified_by=self.user)
47+
Document.objects.create(name="Foo", folder=bar, author=self.user, file=simple_file, modified_by=self.user)
48+
with self.assertRaises(DuplicateDocumentNameError):
49+
Document.objects.create(name="Bar", folder=foo, author=self.user, file=simple_file, modified_by=self.user)

pinax/documents/tests/test_views.py

+30-12
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,10 @@
77
Document,
88
Folder,
99
)
10-
from .test import TestCase
10+
from .test import BaseTest
1111

1212

13-
class TestViews(TestCase):
14-
15-
def setUp(self):
16-
"""
17-
Create default User.
18-
"""
19-
self.user = self.make_user("eldarion")
20-
21-
22-
class TestFolders(TestViews):
13+
class TestFolders(BaseTest):
2314

2415
def setUp(self):
2516
super(TestFolders, self).setUp()
@@ -102,6 +93,19 @@ def test_post_create_with_illegal_parent(self):
10293
self.assertFalse("object" in self.last_response.context)
10394
self.assertFalse(Folder.objects.filter(name=folder_name))
10495

96+
def test_post_create_with_duplicate_name(self):
97+
"""
98+
Ensure POST does not create a folder with duplicate name
99+
"""
100+
folder_name = "Spindle"
101+
Folder.objects.create(name=folder_name, author=self.user, modified_by=self.user)
102+
post_args = {"name": folder_name}
103+
with self.login(self.user):
104+
response = self.post(self.create_urlname, data=post_args, follow=True)
105+
self.response_200(response)
106+
self.assertTrue("{} already exists.".format(folder_name) in str(response.context["form"].errors))
107+
self.assertFalse("object" in self.last_response.context)
108+
105109
def test_detail(self):
106110
"""
107111
Ensure we can see folder detail.
@@ -224,7 +228,7 @@ def test_share_non_author(self):
224228
self.response_404(response)
225229

226230

227-
class TestDocuments(TestViews):
231+
class TestDocuments(BaseTest):
228232

229233
def setUp(self):
230234
super(TestDocuments, self).setUp()
@@ -310,6 +314,20 @@ def test_post_create_with_illegal_parent(self):
310314
self.assertFalse("object" in self.last_response.context)
311315
self.assertFalse(Document.objects.filter(name=simple_file.name))
312316

317+
def test_post_create_with_duplicate_name(self):
318+
"""
319+
Ensure POST does not create a document with duplicate name
320+
"""
321+
simple_file = SimpleUploadedFile("delicious.txt", self.file_contents)
322+
Document.objects.create(name="delicious.txt", author=self.user, file=simple_file, modified_by=self.user)
323+
simple_file = SimpleUploadedFile("delicious.txt", self.file_contents)
324+
post_args = {"name": "file", "file": simple_file}
325+
with self.login(self.user):
326+
response = self.post(self.create_urlname, data=post_args, follow=True)
327+
self.response_200(response)
328+
self.assertTrue("delicious.txt already exists." in str(response.context["form"].errors))
329+
self.assertFalse("object" in self.last_response.context)
330+
313331
def test_detail(self):
314332
"""
315333
Ensure we can see document detail.

0 commit comments

Comments
 (0)