mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #6191, #11296 -- Modified the admin deletion confirmation page to use the same object collection scheme as the actual deletion. This ensures that all objects that may be deleted are actually deleted, and that cyclic display problems are avoided. Thanks to carljm for the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12598 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -16,13 +16,14 @@ from django.utils import formats
|
||||
from django.utils.cache import get_max_age
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import get_date_formats
|
||||
from django.utils.encoding import iri_to_uri
|
||||
|
||||
# local test models
|
||||
from models import Article, BarAccount, CustomArticle, EmptyModel, \
|
||||
ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \
|
||||
Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \
|
||||
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \
|
||||
Category, Post
|
||||
Category, Post, Plot, FunkyTag
|
||||
|
||||
|
||||
class AdminViewBasicTest(TestCase):
|
||||
@@ -637,6 +638,113 @@ class AdminViewPermissionsTest(TestCase):
|
||||
response = self.client.get('/test_admin/admin/secure-view/')
|
||||
self.assertContains(response, 'id="login-form"')
|
||||
|
||||
|
||||
class AdminViewDeletedObjectsTest(TestCase):
|
||||
fixtures = ['admin-views-users.xml', 'deleted-objects.xml']
|
||||
|
||||
def setUp(self):
|
||||
self.client.login(username='super', password='secret')
|
||||
|
||||
def tearDown(self):
|
||||
self.client.logout()
|
||||
|
||||
def test_nesting(self):
|
||||
"""
|
||||
Objects should be nested to display the relationships that
|
||||
cause them to be scheduled for deletion.
|
||||
"""
|
||||
pattern = re.compile(r"""<li>Plot: <a href=".+/admin_views/plot/1/">World Domination</a>\s*<ul>\s*<li>Plot details: <a href=".+/admin_views/plotdetails/1/">almost finished</a>""")
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
|
||||
self.failUnless(pattern.search(response.content))
|
||||
|
||||
def test_cyclic(self):
|
||||
"""
|
||||
Cyclic relationships should still cause each object to only be
|
||||
listed once.
|
||||
|
||||
"""
|
||||
one = """<li>Cyclic one: <a href="/test_admin/admin/admin_views/cyclicone/1/">I am recursive</a>"""
|
||||
two = """<li>Cyclic two: <a href="/test_admin/admin/admin_views/cyclictwo/1/">I am recursive too</a>"""
|
||||
response = self.client.get('/test_admin/admin/admin_views/cyclicone/%s/delete/' % quote(1))
|
||||
|
||||
self.assertContains(response, one, 1)
|
||||
self.assertContains(response, two, 1)
|
||||
|
||||
def test_perms_needed(self):
|
||||
self.client.logout()
|
||||
delete_user = User.objects.get(username='deleteuser')
|
||||
delete_user.user_permissions.add(get_perm(Plot,
|
||||
Plot._meta.get_delete_permission()))
|
||||
|
||||
self.failUnless(self.client.login(username='deleteuser',
|
||||
password='secret'))
|
||||
|
||||
response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(1))
|
||||
self.assertContains(response, "your account doesn't have permission to delete the following types of objects")
|
||||
self.assertContains(response, "<li>plot details</li>")
|
||||
|
||||
|
||||
def test_not_registered(self):
|
||||
should_contain = """<li>Secret hideout: underground bunker"""
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
|
||||
self.assertContains(response, should_contain, 1)
|
||||
|
||||
def test_multiple_fkeys_to_same_model(self):
|
||||
"""
|
||||
If a deleted object has two relationships from another model,
|
||||
both of those should be followed in looking for related
|
||||
objects to delete.
|
||||
|
||||
"""
|
||||
should_contain = """<li>Plot: <a href="/test_admin/admin/admin_views/plot/1/">World Domination</a>"""
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
|
||||
self.assertContains(response, should_contain)
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2))
|
||||
self.assertContains(response, should_contain)
|
||||
|
||||
def test_multiple_fkeys_to_same_instance(self):
|
||||
"""
|
||||
If a deleted object has two relationships pointing to it from
|
||||
another object, the other object should still only be listed
|
||||
once.
|
||||
|
||||
"""
|
||||
should_contain = """<li>Plot: <a href="/test_admin/admin/admin_views/plot/2/">World Peace</a></li>"""
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2))
|
||||
self.assertContains(response, should_contain, 1)
|
||||
|
||||
def test_inheritance(self):
|
||||
"""
|
||||
In the case of an inherited model, if either the child or
|
||||
parent-model instance is deleted, both instances are listed
|
||||
for deletion, as well as any relationships they have.
|
||||
|
||||
"""
|
||||
should_contain = [
|
||||
"""<li>Villain: <a href="/test_admin/admin/admin_views/villain/3/">Bob</a>""",
|
||||
"""<li>Super villain: <a href="/test_admin/admin/admin_views/supervillain/3/">Bob</a>""",
|
||||
"""<li>Secret hideout: floating castle""",
|
||||
"""<li>Super secret hideout: super floating castle!"""
|
||||
]
|
||||
response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(3))
|
||||
for should in should_contain:
|
||||
self.assertContains(response, should, 1)
|
||||
response = self.client.get('/test_admin/admin/admin_views/supervillain/%s/delete/' % quote(3))
|
||||
for should in should_contain:
|
||||
self.assertContains(response, should, 1)
|
||||
|
||||
def test_generic_relations(self):
|
||||
"""
|
||||
If a deleted object has GenericForeignKeys pointing to it,
|
||||
those objects should be listed for deletion.
|
||||
|
||||
"""
|
||||
plot = Plot.objects.get(pk=3)
|
||||
tag = FunkyTag.objects.create(content_object=plot, name='hott')
|
||||
should_contain = """<li>Funky tag: hott"""
|
||||
response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(3))
|
||||
self.assertContains(response, should_contain)
|
||||
|
||||
class AdminViewStringPrimaryKeyTest(TestCase):
|
||||
fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
|
||||
|
||||
@@ -699,7 +807,8 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
||||
def test_deleteconfirmation_link(self):
|
||||
"The link from the delete confirmation page referring back to the changeform of the object should be quoted"
|
||||
response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % quote(self.pk))
|
||||
should_contain = """<a href="../../%s/">%s</a>""" % (quote(self.pk), escape(self.pk))
|
||||
# this URL now comes through reverse(), thus iri_to_uri encoding
|
||||
should_contain = """/%s/">%s</a>""" % (iri_to_uri(quote(self.pk)), escape(self.pk))
|
||||
self.assertContains(response, should_contain)
|
||||
|
||||
def test_url_conflicts_with_add(self):
|
||||
|
||||
Reference in New Issue
Block a user