diff --git a/tailbone/views/master.py b/tailbone/views/master.py index aec92acb..d16a2e73 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -82,6 +82,7 @@ class MasterView(View): editable = True deletable = True bulk_deletable = False + set_deletable = False populatable = False mergeable = False downloadable = False @@ -326,6 +327,14 @@ class MasterView(View): Return a dictionary of kwargs to be passed to the factory when creating new grid instances. """ + permission_prefix = self.get_permission_prefix() + + checkboxes = self.checkboxes + if not checkboxes and self.mergeable and self.request.has_perm('{}.merge'.format(permission_prefix)): + checkboxes = True + if not checkboxes and self.set_deletable and self.request.has_perm('{}.delete_set'.format(permission_prefix)): + checkboxes = True + defaults = { 'model_class': getattr(self, 'model_class', None), 'width': 'full', @@ -335,8 +344,7 @@ class MasterView(View): 'pageable': self.pageable, 'extra_row_class': self.grid_extra_class, 'url': lambda obj: self.get_action_url('view', obj), - 'checkboxes': self.checkboxes or ( - self.mergeable and self.request.has_perm('{}.merge'.format(self.get_permission_prefix()))), + 'checkboxes': checkboxes, 'checked': self.checked, } if 'main_actions' not in kwargs and 'more_actions' not in kwargs: @@ -1569,19 +1577,15 @@ class MasterView(View): """ Delete all records matching the current grid query """ - if self.request.method == 'POST': - objects = self.get_effective_data() - key = '{}.bulk_delete'.format(self.model_class.__tablename__) - progress = SessionProgress(self.request, key) - thread = Thread(target=self.bulk_delete_thread, args=(objects, progress)) - thread.start() - return self.render_progress(progress, { - 'cancel_url': self.get_index_url(), - 'cancel_msg': "Bulk deletion was canceled", - }) - else: - self.request.session.flash("Sorry, you must POST to do a bulk delete operation") - return self.redirect(self.get_index_url()) + objects = self.get_effective_data() + key = '{}.bulk_delete'.format(self.model_class.__tablename__) + progress = SessionProgress(self.request, key) + thread = Thread(target=self.bulk_delete_thread, args=(objects, progress)) + thread.start() + return self.render_progress(progress, { + 'cancel_url': self.get_index_url(), + 'cancel_msg': "Bulk deletion was canceled", + }) def bulk_delete_objects(self, session, objects, progress=None): @@ -1626,6 +1630,26 @@ class MasterView(View): progress.session['success_url'] = self.get_index_url() progress.session.save() + def delete_set(self): + """ + View which can delete a specific set of records. + """ + # TODO: should have a cleaner way to parse object uuids? + uuids = self.request.POST.get('uuids') + if uuids: + uuids = uuids.split(',') + # TODO: probably need to allow override of fetcher callable + fetcher = lambda uuid: self.Session.query(self.model_class).get(uuid) + deleted = 0 + for uuid in uuids: + obj = fetcher(uuid) + if obj: + self.delete_instance(obj) + deleted += 1 + model_title_plural = self.get_model_title_plural() + self.request.session.flash("Deleted {} {}".format(deleted, model_title_plural)) + return self.redirect(self.get_index_url()) + def oneoff_import(self, importer, host_object=None): """ Basic helper method, to do a one-off import (or export, depending on @@ -3057,12 +3081,22 @@ class MasterView(View): # bulk delete if cls.bulk_deletable: - config.add_route('{}.bulk_delete'.format(route_prefix), '{}/bulk-delete'.format(url_prefix)) + config.add_route('{}.bulk_delete'.format(route_prefix), '{}/bulk-delete'.format(url_prefix), + request_method='POST') config.add_view(cls, attr='bulk_delete', route_name='{}.bulk_delete'.format(route_prefix), permission='{}.bulk_delete'.format(permission_prefix)) config.add_tailbone_permission(permission_prefix, '{}.bulk_delete'.format(permission_prefix), "Bulk delete {}".format(model_title_plural)) + # delete set + if cls.set_deletable: + config.add_tailbone_permission(permission_prefix, '{}.delete_set'.format(permission_prefix), + "Delete set (selection) of {}".format(model_title_plural)) + config.add_route('{}.delete_set'.format(route_prefix), '{}/delete-set'.format(url_prefix), + request_method='POST') + config.add_view(cls, attr='delete_set', route_name='{}.delete_set'.format(route_prefix), + permission='{}.delete_set'.format(permission_prefix)) + # merge if cls.mergeable: config.add_route('{}.merge'.format(route_prefix), '{}/merge'.format(url_prefix))