summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/admin/actions.py3
-rw-r--r--django/contrib/admin/options.py3
-rw-r--r--django/contrib/admin/templates/admin/delete_confirmation.html2
-rw-r--r--django/contrib/admin/templates/admin/delete_selected_confirmation.html2
-rw-r--r--django/contrib/admin/templates/admin/includes/object_delete_summary.html7
-rw-r--r--django/contrib/admin/utils.py5
-rw-r--r--docs/releases/1.8.txt4
-rw-r--r--tests/admin_views/tests.py8
8 files changed, 31 insertions, 3 deletions
diff --git a/django/contrib/admin/actions.py b/django/contrib/admin/actions.py
index 158c436c71..3a841bb6c8 100644
--- a/django/contrib/admin/actions.py
+++ b/django/contrib/admin/actions.py
@@ -33,7 +33,7 @@ def delete_selected(modeladmin, request, queryset):
# Populate deletable_objects, a data structure of all related objects that
# will also be deleted.
- deletable_objects, perms_needed, protected = get_deleted_objects(
+ deletable_objects, model_count, perms_needed, protected = get_deleted_objects(
queryset, opts, request.user, modeladmin.admin_site, using)
# The user has already confirmed the deletion.
@@ -67,6 +67,7 @@ def delete_selected(modeladmin, request, queryset):
"title": title,
"objects_name": objects_name,
"deletable_objects": [deletable_objects],
+ "model_count": dict(model_count),
'queryset': queryset,
"perms_lacking": perms_needed,
"protected": protected,
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index f304be0b81..3b429fbaed 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1606,7 +1606,7 @@ class ModelAdmin(BaseModelAdmin):
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
- (deleted_objects, perms_needed, protected) = get_deleted_objects(
+ (deleted_objects, model_count, perms_needed, protected) = get_deleted_objects(
[obj], opts, request.user, self.admin_site, using)
if request.POST: # The user has already confirmed the deletion.
@@ -1631,6 +1631,7 @@ class ModelAdmin(BaseModelAdmin):
object_name=object_name,
object=obj,
deleted_objects=deleted_objects,
+ model_count=dict(model_count),
perms_lacking=perms_needed,
protected=protected,
opts=opts,
diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html
index 6487280d3a..c3816408df 100644
--- a/django/contrib/admin/templates/admin/delete_confirmation.html
+++ b/django/contrib/admin/templates/admin/delete_confirmation.html
@@ -30,6 +30,8 @@
</ul>
{% else %}
<p>{% blocktrans with escaped_object=object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}</p>
+ {% include "admin/includes/object_delete_summary.html" %}
+ <h2>{% trans "Objects" %}</h2>
<ul>{{ deleted_objects|unordered_list }}</ul>
<form action="" method="post">{% csrf_token %}
<div>
diff --git a/django/contrib/admin/templates/admin/delete_selected_confirmation.html b/django/contrib/admin/templates/admin/delete_selected_confirmation.html
index 731c66061d..154e779c49 100644
--- a/django/contrib/admin/templates/admin/delete_selected_confirmation.html
+++ b/django/contrib/admin/templates/admin/delete_selected_confirmation.html
@@ -29,6 +29,8 @@
</ul>
{% else %}
<p>{% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}</p>
+ {% include "admin/includes/object_delete_summary.html" %}
+ <h2>{% trans "Objects" %}</h2>
{% for deletable_object in deletable_objects %}
<ul>{{ deletable_object|unordered_list }}</ul>
{% endfor %}
diff --git a/django/contrib/admin/templates/admin/includes/object_delete_summary.html b/django/contrib/admin/templates/admin/includes/object_delete_summary.html
new file mode 100644
index 0000000000..a2e78ed7a1
--- /dev/null
+++ b/django/contrib/admin/templates/admin/includes/object_delete_summary.html
@@ -0,0 +1,7 @@
+{% load i18n %}
+<h2>{% trans "Summary" %}</h2>
+<ul>
+ {% for model_name, object_count in model_count.items %}
+ <li>{{ model_name|capfirst }}: {{ object_count }}</li>
+ {% endfor %}
+</ul> \ No newline at end of file
diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
index 3c3ef3bbd9..d6756332d3 100644
--- a/django/contrib/admin/utils.py
+++ b/django/contrib/admin/utils.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+from collections import defaultdict
import datetime
import decimal
@@ -154,7 +155,7 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
protected = [format_callback(obj) for obj in collector.protected]
- return to_delete, perms_needed, protected
+ return to_delete, collector.model_count, perms_needed, protected
class NestedObjects(Collector):
@@ -162,6 +163,7 @@ class NestedObjects(Collector):
super(NestedObjects, self).__init__(*args, **kwargs)
self.edges = {} # {from_instance: [to_instances]}
self.protected = set()
+ self.model_count = defaultdict(int)
def add_edge(self, source, target):
self.edges.setdefault(source, []).append(target)
@@ -176,6 +178,7 @@ class NestedObjects(Collector):
self.add_edge(getattr(obj, related_name), obj)
else:
self.add_edge(None, obj)
+ self.model_count[obj._meta.verbose_name_plural] += 1
try:
return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)
except models.ProtectedError as e:
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 5979d79d1d..ae8c1e4d91 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -44,6 +44,10 @@ Minor features
to limit the ``list_filter`` choices to foreign objects which are attached to
those from the ``ModelAdmin``.
+* The :meth:`ModelAdmin.delete_view()
+ <django.contrib.admin.ModelAdmin.delete_view>` displays a summary of objects
+ to be deleted on the deletion confirmation page.
+
:mod:`django.contrib.auth`
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index dfa5a85ae6..adc49a5db0 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -1425,10 +1425,15 @@ class AdminViewPermissionsTest(TestCase):
self.client.get('/test_admin/admin/')
self.client.post(login_url, self.deleteuser_login)
response = self.client.get('/test_admin/admin/admin_views/section/1/delete/')
+ self.assertContains(response, "<h2>Summary</h2>")
+ self.assertContains(response, "<li>Articles: 3</li>")
# test response contains link to related Article
self.assertContains(response, "admin_views/article/1/")
response = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
+ self.assertContains(response, "admin_views/article/1/")
+ self.assertContains(response, "<h2>Summary</h2>")
+ self.assertContains(response, "<li>Articles: 1</li>")
self.assertEqual(response.status_code, 200)
post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
self.assertRedirects(post, '/test_admin/admin/')
@@ -2547,6 +2552,9 @@ class AdminActionsTest(TestCase):
confirmation = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
self.assertIsInstance(confirmation, TemplateResponse)
self.assertContains(confirmation, "Are you sure you want to delete the selected subscribers?")
+ self.assertContains(confirmation, "<h2>Summary</h2>")
+ self.assertContains(confirmation, "<li>Subscribers: 3</li>")
+ self.assertContains(confirmation, "<li>External subscribers: 1</li>")
self.assertContains(confirmation, ACTION_CHECKBOX_NAME, count=2)
self.client.post('/test_admin/admin/admin_views/subscriber/', delete_confirmation_data)
self.assertEqual(Subscriber.objects.count(), 0)