Add initial support for grid index URLs

Yay, been wanting this for some time now.
This commit is contained in:
Lance Edgar 2016-05-01 17:50:57 -05:00
parent 70a2f10c81
commit abb42e9f25
5 changed files with 80 additions and 25 deletions

View file

@ -590,24 +590,23 @@ class Grid(object):
"""
return bool(self.main_actions or self.more_actions)
def render_actions(self, row):
def render_actions(self, row, i):
"""
Returns the rendered contents of the 'actions' column for a given row.
"""
main_actions = filter(None, [self.render_action(a, row) for a in self.main_actions])
more_actions = filter(None, [self.render_action(a, row) for a in self.more_actions])
main_actions = filter(None, [self.render_action(a, row, i) for a in self.main_actions])
more_actions = filter(None, [self.render_action(a, row, i) for a in self.more_actions])
if more_actions:
icon = HTML.tag('span', class_='ui-icon ui-icon-carat-1-e')
link = tags.link_to("More" + icon, '#', class_='more')
main_actions.append(link + HTML.tag('div', class_='more', c=more_actions))
# main_actions.append(tags.link_to("More" + icon + HTML.literal('').join(more_actions), '#', class_='more'))
return HTML.literal('').join(main_actions)
def render_action(self, action, row):
def render_action(self, action, row, i):
"""
Renders an action menu item (link) for the given row.
"""
url = action.get_url(row)
url = action.get_url(row, i)
if url:
kwargs = {'class_': action.key}
if action.icon:
@ -711,10 +710,10 @@ class GridAction(object):
self.icon = icon
self.url = url
def get_url(self, row):
def get_url(self, row, i):
"""
Returns an action URL for the given row.
"""
if callable(self.url):
return self.url(row)
return self.url(row, i)
return self.url

View file

@ -112,6 +112,9 @@ $(function() {
$('input[type=submit]').button();
$('input[type=reset]').button();
/* Also automatically disable any buttons marked for that. */
$('a.button[disabled=disabled]').button('option', 'disabled', true);
/*
* Apply timepicker behavior to text inputs which are marked for it.
*/

View file

@ -5,6 +5,7 @@
<%def name="context_menu_items()">
<li>${h.link_to("Back to {}".format(model_title_plural), index_url)}</li>
<li>${h.link_to("Permalink for this {}".format(model_title), action_url('view', instance))}</li>
% if master.editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)):
<li>${h.link_to("Edit this {}".format(model_title), action_url('edit', instance))}</li>
% endif

View file

@ -25,7 +25,7 @@
% endfor
% if grid.show_actions_column:
<td class="actions">
${grid.render_actions(row)}
${grid.render_actions(row, i)}
</td>
% endif
</tr>

View file

@ -64,6 +64,9 @@ class MasterView(View):
row_attrs = {}
cell_attrs = {}
grid_index = None
use_index_links = False
@property
def Session(self):
"""
@ -93,10 +96,16 @@ class MasterView(View):
if self.request.GET.get('reset-to-default-filters') == 'true':
return self.redirect(self.request.current_route_url(_query=None))
# Stash some grid stats, for possible use when generating URLs.
if grid.pageable and grid.pager:
self.first_visible_grid_index = grid.pager.first_item
# Return grid only, if partial page was requested.
if self.request.params.get('partial'):
self.request.response.content_type = b'text/html'
self.request.response.text = grid.render_grid()
return self.request.response
return self.render_to_response('index', {'grid': grid})
def create(self):
@ -122,11 +131,12 @@ class MasterView(View):
def redirect_after_create(self, instance):
return self.redirect(self.get_action_url('view', instance))
def view(self):
def view(self, instance=None):
"""
View for viewing details of an existing model record.
"""
self.viewing = True
if instance is None:
instance = self.get_instance()
form = self.make_form(instance)
return self.render_to_response('view', {
@ -136,6 +146,28 @@ class MasterView(View):
'instance_deletable': self.deletable_instance(instance),
'form': form})
def view_index(self):
"""
View a record according to its grid index.
"""
try:
index = int(self.request.GET['index'])
except KeyError, ValueError:
return self.redirect(self.get_index_url())
if index < 1:
return self.redirect(self.get_index_url())
data = self.get_effective_data()
try:
instance = data[index-1]
except IndexError:
return self.redirect(self.get_index_url())
self.grid_index = index
if hasattr(data, 'count'):
self.grid_count = data.count()
else:
self.grid_count = len(data)
return self.view(instance)
def edit(self):
"""
View for editing an existing model record.
@ -327,7 +359,10 @@ class MasterView(View):
'index_title': self.get_index_title(),
'index_url': self.get_index_url(),
'action_url': self.get_action_url,
'grid_index': self.grid_index,
}
if self.grid_index:
context['grid_count'] = self.grid_count
context.update(data)
context.update(self.template_kwargs(**context))
if hasattr(self, 'template_kwargs_{}'.format(template)):
@ -445,9 +480,14 @@ class MasterView(View):
actions = []
prefix = self.get_permission_prefix()
if self.viewable and self.request.has_perm('{}.view'.format(prefix)):
actions.append(self.make_action('view', icon='zoomin'))
url = self.get_view_index_url if self.use_index_links else None
actions.append(self.make_action('view', icon='zoomin', url=url))
return actions
def get_view_index_url(self, row, i):
route = '{}.view_index'.format(self.get_route_prefix())
return '{}?index={}'.format(self.request.route_url(route), self.first_visible_grid_index + i - 1)
def get_more_actions(self):
"""
Return a list of 'more' actions for the grid.
@ -460,19 +500,19 @@ class MasterView(View):
actions.append(self.make_action('delete', icon='trash', url=self.default_delete_url))
return actions
def default_delete_url(self, row):
def default_delete_url(self, row, i=None):
if self.deletable_instance(row):
return self.request.route_url('{}.delete'.format(self.get_route_prefix()),
**self.get_action_route_kwargs(row))
def make_action(self, key, **kwargs):
def make_action(self, key, url=None, **kwargs):
"""
Make a new :class:`GridAction` instance for the current grid.
"""
kwargs.setdefault('url', lambda r: self.request.route_url(
'{0}.{1}'.format(self.get_route_prefix(), key),
**self.get_action_route_kwargs(r)))
return GridAction(key, **kwargs)
if url is None:
route = '{}.{}'.format(self.get_route_prefix(), key)
url = lambda r, i: self.request.route_url(route, **self.get_action_route_kwargs(r))
return GridAction(key, url=url, **kwargs)
def get_action_route_kwargs(self, row):
"""
@ -542,16 +582,21 @@ class MasterView(View):
"""
return session.query(self.get_model_class())
def get_effective_query(self, session):
def get_effective_data(self, session=None):
"""
Convenience method which returns the "effective" query for the master
Convenience method which returns the "effective" data for the master
grid, filtered and sorted to match what would show on the UI, but not
paged etc.
"""
if session is None:
session = self.Session()
grid = self.make_grid(session=session, pageable=False,
main_actions=[], more_actions=[])
return grid._fa_grid.rows
def get_effective_query(self, session):
return self.get_effective_data(session)
def checkbox(self, instance):
"""
Returns a boolean indicating whether ot not a checkbox should be
@ -738,11 +783,18 @@ class MasterView(View):
# view
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))
config.add_tailbone_permission(permission_prefix, '{}.view'.format(permission_prefix),
"View {} details".format(model_title))
# view by grid index
config.add_route('{}.view_index'.format(route_prefix), '{}/view'.format(url_prefix))
config.add_view(cls, attr='view_index', route_name='{}.view_index'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
# view by record key
config.add_route('{}.view'.format(route_prefix), '{}/{{{}}}'.format(url_prefix, model_key))
config.add_view(cls, attr='view', route_name='{}.view'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
# edit
if cls.editable: