Add progress support for bulk deletion
plus bulk-delete all tempmon readings when deleting client or probe
This commit is contained in:
parent
d8be651e95
commit
3205d61ba6
|
@ -29,6 +29,7 @@ from __future__ import unicode_literals, absolute_import
|
|||
import os
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.util import progress_loop
|
||||
|
||||
from pyramid import httpexceptions
|
||||
from pyramid.renderers import render_to_response
|
||||
|
@ -86,6 +87,9 @@ class View(object):
|
|||
"""
|
||||
return httpexceptions.HTTPFound(location=url, **kwargs)
|
||||
|
||||
def progress_loop(self, func, items, factory, *args, **kwargs):
|
||||
return progress_loop(func, items, factory, *args, **kwargs)
|
||||
|
||||
def render_progress(self, kwargs):
|
||||
"""
|
||||
Render the progress page, with given kwargs as context.
|
||||
|
|
|
@ -32,9 +32,11 @@ from sqlalchemy import orm
|
|||
|
||||
import sqlalchemy_continuum as continuum
|
||||
|
||||
from rattail.db import Session as RattailSession
|
||||
from rattail.db.continuum import model_transaction_query
|
||||
from rattail.util import prettify
|
||||
from rattail.time import localtime
|
||||
from rattail.threads import Thread
|
||||
|
||||
import formalchemy as fa
|
||||
from pyramid import httpexceptions
|
||||
|
@ -43,6 +45,7 @@ from webhelpers2.html import HTML, tags
|
|||
|
||||
from tailbone import forms, grids
|
||||
from tailbone.views import View
|
||||
from tailbone.progress import SessionProgress
|
||||
|
||||
|
||||
class MasterView(View):
|
||||
|
@ -702,20 +705,60 @@ class MasterView(View):
|
|||
Delete all records matching the current grid query
|
||||
"""
|
||||
if self.request.method == 'POST':
|
||||
query = self.get_effective_query(sortable=False)
|
||||
count = query.count()
|
||||
self.bulk_delete_objects(query)
|
||||
self.request.session.flash("Deleted {:,d} {}".format(count, self.get_model_title_plural()))
|
||||
key = '{}.bulk_delete'.format(self.model_class.__tablename__)
|
||||
objects = self.get_effective_data()
|
||||
progress = SessionProgress(self.request, key)
|
||||
thread = Thread(target=self.bulk_delete_thread, args=(objects, progress))
|
||||
thread.start()
|
||||
return self.render_progress({
|
||||
'key': key,
|
||||
'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())
|
||||
|
||||
def bulk_delete_objects(self, query):
|
||||
# TODO: sometimes the first makes sense, and would be preferred for
|
||||
# efficiency's sake. might even need to add progress to latter?
|
||||
# query.delete(synchronize_session=False)
|
||||
for obj in query:
|
||||
self.Session.delete(obj)
|
||||
def bulk_delete_objects(self, session, objects, progress=None):
|
||||
|
||||
def delete(obj, i):
|
||||
session.delete(obj)
|
||||
|
||||
self.progress_loop(delete, objects, progress,
|
||||
message="Deleting objects")
|
||||
|
||||
def get_bulk_delete_session(self):
|
||||
return RattailSession()
|
||||
|
||||
def bulk_delete_thread(self, objects, progress):
|
||||
"""
|
||||
Thread target for bulk-deleting current results, with progress.
|
||||
"""
|
||||
session = self.get_bulk_delete_session()
|
||||
objects = objects.with_session(session).all()
|
||||
try:
|
||||
self.bulk_delete_objects(session, objects, progress=progress)
|
||||
|
||||
# If anything goes wrong, rollback and log the error etc.
|
||||
except Exception as error:
|
||||
session.rollback()
|
||||
log.exception("execution failed for batch results")
|
||||
session.close()
|
||||
if progress:
|
||||
progress.session.load()
|
||||
progress.session['error'] = True
|
||||
progress.session['error_msg'] = "Bulk deletion failed: {}: {}".format(type(error).__name__, error)
|
||||
progress.session.save()
|
||||
|
||||
# If no error, check result flag (false means user canceled).
|
||||
else:
|
||||
session.commit()
|
||||
session.close()
|
||||
if progress:
|
||||
progress.session.load()
|
||||
progress.session['complete'] = True
|
||||
progress.session['success_url'] = self.get_index_url()
|
||||
progress.session.save()
|
||||
|
||||
def get_merge_fields(self):
|
||||
if hasattr(self, 'merge_fields'):
|
||||
|
|
|
@ -114,6 +114,19 @@ class TempmonClientView(MasterView):
|
|||
del fs.probes
|
||||
del fs.online
|
||||
|
||||
def delete_instance(self, client):
|
||||
# bulk-delete all readings first
|
||||
readings = self.Session.query(tempmon.Reading)\
|
||||
.filter(tempmon.Reading.client == client)
|
||||
readings.delete(synchronize_session=False)
|
||||
self.Session.flush()
|
||||
self.Session.refresh(client)
|
||||
|
||||
# Flush immediately to force any pending integrity errors etc.; that
|
||||
# way we don't set flash message until we know we have success.
|
||||
self.Session.delete(client)
|
||||
self.Session.flush()
|
||||
|
||||
def restartable_client(self, client):
|
||||
return True
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ Common stuff for tempmon views
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail_tempmon.db import Session as RawTempmonSession
|
||||
|
||||
from formalchemy.fields import SelectFieldRenderer
|
||||
from webhelpers2.html import tags
|
||||
|
||||
|
@ -39,6 +41,9 @@ class MasterView(views.MasterView2):
|
|||
"""
|
||||
Session = TempmonSession
|
||||
|
||||
def get_bulk_delete_session(self):
|
||||
return RawTempmonSession()
|
||||
|
||||
|
||||
class ClientFieldRenderer(SelectFieldRenderer):
|
||||
|
||||
|
|
|
@ -109,6 +109,19 @@ class TempmonProbeView(MasterView):
|
|||
if self.creating or self.editing:
|
||||
del fs.status
|
||||
|
||||
def delete_instance(self, probe):
|
||||
# bulk-delete all readings first
|
||||
readings = self.Session.query(tempmon.Reading)\
|
||||
.filter(tempmon.Reading.probe == probe)
|
||||
readings.delete(synchronize_session=False)
|
||||
self.Session.flush()
|
||||
self.Session.refresh(probe)
|
||||
|
||||
# Flush immediately to force any pending integrity errors etc.; that
|
||||
# way we don't set flash message until we know we have success.
|
||||
self.Session.delete(probe)
|
||||
self.Session.flush()
|
||||
|
||||
|
||||
def includeme(config):
|
||||
TempmonProbeView.defaults(config)
|
||||
|
|
Loading…
Reference in a new issue