For #161, this removes the FBV and implements an easily extendible BaseListAction class

This commit is contained in:
Daniel Greenfeld 2013-05-31 16:47:25 +02:00
parent 1916f7cba4
commit 0da6f453b7
5 changed files with 85 additions and 55 deletions

View file

@ -14,7 +14,7 @@ def get_description(action):
return capfirst(action.__name__.replace('_', ' '))
def delete_selected(request, queryset):
class BaseListAction(object):
# We check whether the user has permission to delete the objects in the
# queryset.
#
@ -25,52 +25,71 @@ def delete_selected(request, queryset):
# `get_deleted_objects` in contrib.admin.util for how this is currently
# done. (Hint: I think we can do better.)
model = queryset.model
opts = utils.model_options(model)
permission_name = '%s.delete.%s' \
% (opts.app_label, opts.object_name.lower())
has_permission = request.user.has_perm(permission_name)
if len(queryset) == 1:
objects_name = opts.verbose_name
else:
objects_name = opts.verbose_name_plural
objects_name = unicode(objects_name)
if request.POST.get('confirmed'):
# The user has confirmed that they want to delete the objects.
if has_permission:
num_objects_deleted = len(queryset)
queryset.delete()
message = "Successfully deleted %d %s" % \
(num_objects_deleted, objects_name)
messages.add_message(request, messages.INFO, message)
return None
def __init__(self, request, queryset):
self.request = request
self.queryset = queryset
self.model = queryset.model
self.options = utils.model_options(self.model)
self.permission_name = '%s.delete.%s' \
% (self.options.app_label, self.options.object_name.lower())
self.has_permission = request.user.has_perm(self.permission_name)
if queryset.count() == 1:
objects_name = self.options.verbose_name
else:
raise PermissionDenied
else:
# The user has not confirmed that they want to delete the objects, so
# render a template asking for their confirmation.
if has_permission:
template = 'admin2/bootstrap/delete_selected_confirmation.html'
objects_name = self.options.verbose_name_plural
self.objects_name = unicode(objects_name)
def _format_callback(obj):
opts = utils.model_options(obj)
return '%s: %s' % (force_text(capfirst(opts.verbose_name)),
force_text(obj))
def description(self):
return NotImplemented
collector = utils.NestedObjects(using=None)
collector.collect(queryset)
def get_response(self):
return NotImplemented
context = {
'queryset': queryset,
'objects_name': objects_name,
'deletable_objects': collector.nested(_format_callback),
}
return TemplateResponse(request, template, context)
def get_template(self):
return NotImplemented
def __call__(self):
return self.get_response()
class DeleteSelectedAction(BaseListAction):
description = "Delete selected items"
def get_response(self):
if self.request.POST.get('confirmed'):
# The user has confirmed that they want to delete the objects.
if self.has_permission:
num_objects_deleted = len(self.queryset)
self.queryset.delete()
message = "Successfully deleted %d %s" % \
(num_objects_deleted, self.objects_name)
messages.add_message(self.request, messages.INFO, message)
return None
else:
raise PermissionDenied
else:
message = "Permission to delete %s denied" % objects_name
messages.add_message(request, messages.INFO, message)
return None
# The user has not confirmed that they want to delete the objects, so
# render a template asking for their confirmation.
if self.has_permission:
template = 'admin2/bootstrap/actions/delete_selected_confirmation.html'
def _format_callback(obj):
opts = utils.model_options(obj)
return '%s: %s' % (force_text(capfirst(opts.verbose_name)),
force_text(obj))
collector = utils.NestedObjects(using=None)
collector.collect(self.queryset)
context = {
'queryset': self.queryset,
'objects_name': self.objects_name,
'deletable_objects': collector.nested(_format_callback),
}
return TemplateResponse(self.request, template, context)
else:
message = "Permission to delete %s denied" % self.objects_name
messages.add_message(self.request, messages.INFO, message)
return None
delete_selected.description = "Delete selected items"

View file

@ -16,7 +16,7 @@
<form method="post">
{% csrf_token %}
<input type="hidden" name="confirmed" value="yes" />
<input type="hidden" name="action" value="delete_selected" />
<input type="hidden" name="action" value="DeleteSelectedAction" />
{% for item in queryset %}
<input type="hidden" name="selected_model_pk" value="{{ item.pk }}" />
{% endfor %}

View file

@ -45,7 +45,7 @@ class ModelAdmin2(object):
list_fields = []
#This shows up on the DocumentListView of the Posts
list_actions = [actions.delete_selected]
list_actions = [actions.DeleteSelectedAction]
# This shows up in the DocumentDetailView of the Posts.
document_actions = []
@ -220,7 +220,7 @@ class ModelAdmin2(object):
actions_dict[action.__name__] = {
'name': action.__name__,
'description': actions.get_description(action),
'func': action
'action_class': action
}
return actions_dict

View file

@ -52,13 +52,24 @@ class ModelListView(AdminModel2Mixin, generic.ListView):
permissions.IsStaffPermission,
permissions.ModelViewPermission)
# def post(self, request):
# # This is where we handle actions
# action_name = request.POST['action']
# action_func = self.get_actions()[action_name]['func']
# selected_model_pks = request.POST.getlist('selected_model_pk')
# queryset = self.model.objects.filter(pk__in=selected_model_pks)
# response = action_func(request, queryset)
# if response is None:
# return HttpResponseRedirect(self.get_success_url())
# else:
# return response
def post(self, request):
# This is where we handle actions
action_name = request.POST['action']
action_func = self.get_actions()[action_name]['func']
action_class = self.get_actions()[action_name]['action_class']
selected_model_pks = request.POST.getlist('selected_model_pk')
queryset = self.model.objects.filter(pk__in=selected_model_pks)
response = action_func(request, queryset)
response = action_class(request, queryset)()
if response is None:
return HttpResponseRedirect(self.get_success_url())
else:

View file

@ -32,17 +32,17 @@ class PostListTest(BaseIntegrationTest):
def test_actions_displayed(self):
response = self.client.get(reverse("admin2:blog_post_index"))
self.assertInHTML('<option value="delete_selected">Delete selected items</option>', response.content)
self.assertInHTML('<option value="DeleteSelectedAction">Delete selected items</option>', response.content)
def test_delete_selected_post(self):
post = Post.objects.create(title="a_post_title", body="body")
params = {'action': 'delete_selected', 'selected_model_pk': str(post.pk)}
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk)}
response = self.client.post(reverse("admin2:blog_post_index"), params)
self.assertInHTML('<p>Are you sure you want to delete the selected post? All of the following items will be deleted:</p>', response.content)
def test_delete_selected_post_confirmation(self):
post = Post.objects.create(title="a_post_title", body="body")
params = {'action': 'delete_selected', 'selected_model_pk': str(post.pk), 'confirmed': 'yes'}
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk), 'confirmed': 'yes'}
response = self.client.post(reverse("admin2:blog_post_index"), params)
self.assertRedirects(response, reverse("admin2:blog_post_index"))
@ -140,7 +140,7 @@ class PostDeleteActionTest(BaseIntegrationTest):
p1 = Post.objects.create(title="A Post Title", body="body")
p2 = Post.objects.create(title="A Post Title", body="body")
post_data = {
'action': 'delete_selected',
'action': 'DeleteSelectedAction',
'selected_model_pk': [p1.pk, p2.pk]
}
response = self.client.post(reverse("admin2:blog_post_index"),
@ -152,7 +152,7 @@ class PostDeleteActionTest(BaseIntegrationTest):
p1 = Post.objects.create(title="A Post Title", body="body")
p2 = Post.objects.create(title="A Post Title", body="body")
post_data = {
'action': 'delete_selected',
'action': 'DeleteSelectedAction',
'selected_model_pk': [p1.pk, p2.pk],
'confirmed': 'yes'
}