Add basic checkbox support to new grids.

Also:

 * Add 'creatable', 'editable' etc. to master view class.
 * Add styles for warning/notice grid rows.
 * Misc. other tweaks.
This commit is contained in:
Lance Edgar 2015-08-14 15:30:38 -05:00
parent e79531fda8
commit d2b065a8fc
13 changed files with 229 additions and 71 deletions

View file

@ -45,11 +45,19 @@ class MasterView(View):
"""
Base "master" view class. All model master views should derive from this.
"""
creatable = True
viewable = True
editable = True
deletable = True
creating = False
viewing = False
editing = False
deleting = False
row_attrs = {}
cell_attrs = {}
##############################
# Available Views
##############################
@ -123,13 +131,10 @@ class MasterView(View):
if result is not None:
return result
# Flush immediately to force any pending integrity errors etc.; that
# way we don't set flash message until we know we have success.
Session.delete(instance)
Session.flush()
self.delete_instance(instance)
self.request.session.flash("{0} {1} has been deleted.".format(
self.get_model_title(), instance))
return HTTPFound(location=self.get_index_url())
return self.redirect(self.get_after_delete_url(instance))
##############################
@ -237,6 +242,7 @@ class MasterView(View):
the template prefix.
"""
data.update({
'master': self,
'model_title': self.get_model_title(),
'model_title_plural': self.get_model_title_plural(),
'route_prefix': self.get_route_prefix(),
@ -300,28 +306,55 @@ class MasterView(View):
'pageable': True,
'main_actions': self.get_main_actions(),
'more_actions': self.get_more_actions(),
'checkbox': self.checkbox,
'checked': self.checked,
'row_attrs': self.get_row_attrs,
'cell_attrs': self.get_cell_attrs,
'model_title': self.get_model_title(),
'model_title_plural': self.get_model_title_plural(),
'permission_prefix': self.get_permission_prefix(),
'route_prefix': self.get_route_prefix(),
}
def get_row_attrs(self, row, i):
"""
Returns a dict of HTML attributes which is to be applied to the row's
``<tr>`` element. Note that ``i`` will be a 1-based index value for
the row within its table. The meaning of ``row`` is basically not
defined; it depends on the type of data the grid deals with.
"""
if callable(self.row_attrs):
return self.row_attrs(row, i)
return self.row_attrs
def get_cell_attrs(self, row, column):
"""
Returns a dictionary of HTML attributes which should be applied to the
``<td>`` element in which the given row and column "intersect".
"""
if callable(self.cell_attrs):
return self.cell_attrs(row, column)
return self.cell_attrs
def get_main_actions(self):
"""
Return a list of 'main' actions for the grid.
"""
return [
self.make_action('view', icon='zoomin'),
]
actions = []
if self.viewable:
actions.append(self.make_action('view', icon='zoomin'))
return actions
def get_more_actions(self):
"""
Return a list of 'more' actions for the grid.
"""
return [
self.make_action('edit', icon='pencil'),
self.make_action('delete', icon='trash'),
]
actions = []
if self.editable:
actions.append(self.make_action('edit', icon='pencil'))
if self.deletable:
actions.append(self.make_action('delete', icon='trash'))
return actions
def make_action(self, key, **kwargs):
"""
@ -350,6 +383,7 @@ class MasterView(View):
data = self.make_query()
kwargs = self.make_grid_kwargs()
grid = factory(key, self.request, data=data, model_class=self.model_class, **kwargs)
grid._fa_grid.prettify = prettify
self.configure_grid(grid)
grid.load_settings()
return grid
@ -387,6 +421,21 @@ class MasterView(View):
"""
return session.query(self.model_class)
def checkbox(self, instance):
"""
Returns a boolean indicating whether ot not a checkbox should be
rendererd for the given row. Default implementation returns ``True``
in all cases.
"""
return True
def checked(self, instance):
"""
Returns a boolean indicating whether ot not a checkbox should be
checked by default, for the given row. Default implementation returns
``False`` in all cases.
"""
return False
##############################
# CRUD Stuff
@ -445,11 +494,39 @@ class MasterView(View):
Event hook, called just after an existing instance is saved.
"""
def deletable_instance(self, instance):
"""
Returns boolean indicating whether or not the given instance can be
considered "deletable". Returns ``True`` by default; override as
necessary.
"""
return True
def before_delete(self, instance):
"""
Event hook, called just before deletion is attempted.
"""
def delete_instance(self, instance):
"""
Delete the instance, or mark it as deleted, or whatever you need to do.
"""
# Flush immediately to force any pending integrity errors etc.; that
# way we don't set flash message until we know we have success.
Session.delete(instance)
Session.flush()
def get_after_delete_url(self, instance):
"""
Returns the URL to which the user should be redirected after
successfully "deleting" the given instance.
"""
if hasattr(self, 'after_delete_url'):
if callable(self.after_delete_url):
return self.after_delete_url(instance)
return self.after_delete_url
return self.get_index_url()
##############################
# Config Stuff
##############################
@ -476,29 +553,33 @@ class MasterView(View):
"List/Search {0}".format(model_title_plural))
# create
config.add_route('{0}.create'.format(route_prefix), '{0}/new'.format(url_prefix))
config.add_view(cls, attr='create', route_name='{0}.create'.format(route_prefix),
permission='{0}.create'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.create'.format(permission_prefix),
"Create new {0}".format(model_title_plural))
if cls.creatable:
config.add_route('{0}.create'.format(route_prefix), '{0}/new'.format(url_prefix))
config.add_view(cls, attr='create', route_name='{0}.create'.format(route_prefix),
permission='{0}.create'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.create'.format(permission_prefix),
"Create new {0}".format(model_title))
# view
config.add_route('{0}.view'.format(route_prefix), '{0}/{{{1}}}'.format(url_prefix, model_key))
config.add_view(cls, attr='view', route_name='{0}.view'.format(route_prefix),
permission='{0}.view'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.view'.format(permission_prefix),
"View {0} Details".format(model_title))
if cls.viewable:
config.add_route('{0}.view'.format(route_prefix), '{0}/{{{1}}}'.format(url_prefix, model_key))
config.add_view(cls, attr='view', route_name='{0}.view'.format(route_prefix),
permission='{0}.view'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.view'.format(permission_prefix),
"View {0} Details".format(model_title))
# edit
config.add_route('{0}.edit'.format(route_prefix), '{0}/{{{1}}}/edit'.format(url_prefix, model_key))
config.add_view(cls, attr='edit', route_name='{0}.edit'.format(route_prefix),
permission='{0}.edit'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.edit'.format(permission_prefix),
"Edit {0}".format(model_title_plural))
if cls.editable:
config.add_route('{0}.edit'.format(route_prefix), '{0}/{{{1}}}/edit'.format(url_prefix, model_key))
config.add_view(cls, attr='edit', route_name='{0}.edit'.format(route_prefix),
permission='{0}.edit'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.edit'.format(permission_prefix),
"Edit {0}".format(model_title))
# delete
config.add_route('{0}.delete'.format(route_prefix), '{0}/{{{1}}}/delete'.format(url_prefix, model_key))
config.add_view(cls, attr='delete', route_name='{0}.delete'.format(route_prefix),
permission='{0}.delete'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
"Delete {0}".format(model_title_plural))
if cls.deletable:
config.add_route('{0}.delete'.format(route_prefix), '{0}/{{{1}}}/delete'.format(url_prefix, model_key))
config.add_view(cls, attr='delete', route_name='{0}.delete'.format(route_prefix),
permission='{0}.delete'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
"Delete {0}".format(model_title))