Handle actions by POSTing to ModelListView

This commit is contained in:
Peter Inglesby 2013-05-22 11:09:01 +01:00
parent 79e6dfad83
commit ade92956d0
5 changed files with 125 additions and 32 deletions

View file

@ -13,11 +13,13 @@ from django.db.models import get_models, signals
from djadmin2 import apiviews
from djadmin2 import views
from djadmin2 import actions
MODEL_ADMIN_ATTRS = (
'list_display', 'list_display_links', 'list_filter',
'admin', 'has_permission', 'has_add_permission',
'has_edit_permission', 'has_delete_permission',
'get_actions',
)
@ -126,6 +128,9 @@ class ModelAdmin2(BaseAdmin2):
api_list_view = apiviews.ListCreateAPIView
api_detail_view = apiviews.RetrieveUpdateDestroyAPIView
# Actions
actions = [actions.delete_selected]
def __init__(self, model, admin, **kwargs):
self.model = model
self.admin = admin
@ -243,6 +248,20 @@ class ModelAdmin2(BaseAdmin2):
def api_urls(self):
return self.get_api_urls(), None, None
def get_actions(self):
actions_dict = {}
for cls in type(self).mro()[::-1]:
class_actions = getattr(cls, 'actions', [])
for action in class_actions:
actions_dict[action.__name__] = {
'name': action.__name__,
'description': actions.get_description(action),
'func': action
}
return actions_dict
class ImmutableAdmin(object):
"""

View file

@ -0,0 +1,26 @@
$(function() {
var element = $("#model-list");
var selectAllCheckbox = element.find('.model-select-all');
var selectCheckboxen = element.find('.model-select');
var selectedCount = element.find('#selected-count');
var updateSelectedCount = function() {
var count = 0;
for (var ix = 0; ix < selectCheckboxen.length; ix++) {
if ($(selectCheckboxen[ix]).prop('checked')) {
count++;
};
};
selectAllCheckbox.prop('checked', count == selectCheckboxen.length);
selectedCount.text(count);
};
selectAllCheckbox.click(function(e) {
selectCheckboxen.prop('checked', this.checked);
updateSelectedCount();
});
selectCheckboxen.click(function(e) {
updateSelectedCount();
});
});

View file

@ -5,46 +5,57 @@
{% block page_title %}Select {{ model }} to change{% endblock %}
{% block extrajs %}
<script src="/static/themes/bootstrap/js/actions.js"></script>
{% endblock extrajs %}
{% block content %}
<div class="row">
<div class="span12">
<div class="space-below">
<div class="btn-group">
<button class="btn dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a tabindex="-1" href="#">Delete selected {{ model }}{{ object_list|pluralize }}</a></li>
</ul>
<div id="model-list" class="row">
<form id="model-list-form" class="form-inline" method="post">
<div class="span12">
<div class="navbar">
{% csrf_token %}
<select name="action">
{% for action in actions %}
<option value="{{ action.name }}">{{ action.description }}</option>
{% endfor %}
</select>
<button type="Submit" class="btn">Go</button>
<span id="selected-count">0</span> of {{ object_list|length }} selected
<div class="pull-right">
{# if has_add_permission #}
<a href="{% url view|admin2_urlname:'create' %}" class="btn"><i class="icon-plus"></i> Add {{ model }}</a>
{# endif #}
</div>
</div>
<small class="muted">TODO of {{ object_list|length }} selected</small>
<div class="pull-right">
{# if has_add_permission #}
<a href="{% url view|admin2_urlname:'create' %}" class="btn"><i class="icon-plus"></i> Add {{ model }}</a>
{# endif #}
</div>
</div>
<table class="table table-bordered table-striped">
<thead>
<th class="checkbox-column"><input type="checkbox"></th>
<th>{{ model|title}}</th>
</thead>
<tbody>
{% for obj in object_list %}
<table class="table table-bordered table-striped">
<thead>
<th><input type="checkbox" class="model-select-all"></th>
<th>{{ model|title}}</th>
</thead>
<tbody>
{% for obj in object_list %}
<tr>
<td><input type="checkbox"></td>
<td><input type="checkbox" class="model-select" name="selected_model_id" value="{{obj.id}}"></td>
<td>
<a href="{% url view|admin2_urlname:'update' pk=obj.pk %}">{{ obj }}</a>
{{ obj }} <a href="{% url view|admin2_urlname:'detail' pk=obj.pk %}">detail</a>
{# if has_edit_permission #}
<a href="{% url view|admin2_urlname:'update' pk=obj.pk %}">edit</a>
{# endif #}
{# if has_delete_permission #}
<a href="{% url view|admin2_urlname:'delete' pk=obj.pk %}">delete</a>
{# endif #}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>{{ object_list|length }} {{ model }}{{ object_list|pluralize }}</p>
</div>
{% endfor %}
</tbody>
</table>
{{ object_list|length }} {{ model }}{{ object_list|pluralize }}
</div>
</form>
</div>
{% endblock content %}

View file

@ -5,6 +5,7 @@ from django.conf import settings
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import PermissionDenied
from django.forms.models import modelform_factory
from django.http import HttpResponseRedirect
from django.views import generic
from braces.views import AccessMixin
@ -109,12 +110,32 @@ class ModelListView(Admin2Mixin, generic.ListView):
default_template_name = "model_list.html"
permission_type = 'view'
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_ids = request.POST.getlist('selected_model_id')
queryset = self.model.objects.filter(pk__in=selected_model_ids)
response = action_func(request, queryset)
if response is None:
return HttpResponseRedirect(self.get_success_url())
else:
return response
def get_context_data(self, **kwargs):
context = super(ModelListView, self).get_context_data(**kwargs)
context['model'] = self.get_model()._meta.verbose_name
context['model_pluralized'] = self.get_model()._meta.verbose_name_plural
context['actions'] = self.get_actions().values()
return context
def get_success_url(self):
view_name = 'admin2:{}_{}_index'.format(self.app_label, self.model_name)
return reverse(view_name)
def get_actions(self):
return self.model_admin.get_actions()
class ModelDetailView(AdminModel2Mixin, generic.DetailView):
default_template_name = "model_detail.html"

View file

@ -30,6 +30,22 @@ class PostListTest(BaseIntegrationTest):
response = self.client.get(reverse("admin2:blog_post_index"))
self.assertContains(response, post.title)
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)
def test_delete_selected_post(self):
post = Post.objects.create(title="a_post_title", body="body")
params = {'action': 'delete_selected', 'selected_model_id': str(post.id)}
response = self.client.post(reverse("admin2:blog_post_index"), params)
self.assertInHTML('<p>Are you sure you want to delete the selected post?</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_id': str(post.id), 'confirmed': 'yes'}
response = self.client.post(reverse("admin2:blog_post_index"), params)
self.assertRedirects(response, reverse("admin2:blog_post_index"))
class PostDetailViewTest(BaseIntegrationTest):
def test_view_ok(self):