mirror of https://github.com/django/django.git
Fixed #20133 -- Added summary to admin deletion confirmation pages.
Thanks jonash for the suggestion and initial patch.
This commit is contained in:
parent
2a4492aecb
commit
3021453285
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
|
@ -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:
|
||||
|
|
|
@ -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`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue