Add initial support for grid index URLs
Yay, been wanting this for some time now.
This commit is contained in:
parent
70a2f10c81
commit
abb42e9f25
|
@ -590,24 +590,23 @@ class Grid(object):
|
||||||
"""
|
"""
|
||||||
return bool(self.main_actions or self.more_actions)
|
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.
|
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])
|
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) for a in self.more_actions])
|
more_actions = filter(None, [self.render_action(a, row, i) for a in self.more_actions])
|
||||||
if more_actions:
|
if more_actions:
|
||||||
icon = HTML.tag('span', class_='ui-icon ui-icon-carat-1-e')
|
icon = HTML.tag('span', class_='ui-icon ui-icon-carat-1-e')
|
||||||
link = tags.link_to("More" + icon, '#', class_='more')
|
link = tags.link_to("More" + icon, '#', class_='more')
|
||||||
main_actions.append(link + HTML.tag('div', class_='more', c=more_actions))
|
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)
|
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.
|
Renders an action menu item (link) for the given row.
|
||||||
"""
|
"""
|
||||||
url = action.get_url(row)
|
url = action.get_url(row, i)
|
||||||
if url:
|
if url:
|
||||||
kwargs = {'class_': action.key}
|
kwargs = {'class_': action.key}
|
||||||
if action.icon:
|
if action.icon:
|
||||||
|
@ -711,10 +710,10 @@ class GridAction(object):
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
def get_url(self, row):
|
def get_url(self, row, i):
|
||||||
"""
|
"""
|
||||||
Returns an action URL for the given row.
|
Returns an action URL for the given row.
|
||||||
"""
|
"""
|
||||||
if callable(self.url):
|
if callable(self.url):
|
||||||
return self.url(row)
|
return self.url(row, i)
|
||||||
return self.url
|
return self.url
|
||||||
|
|
|
@ -112,6 +112,9 @@ $(function() {
|
||||||
$('input[type=submit]').button();
|
$('input[type=submit]').button();
|
||||||
$('input[type=reset]').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.
|
* Apply timepicker behavior to text inputs which are marked for it.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
<%def name="context_menu_items()">
|
<%def name="context_menu_items()">
|
||||||
<li>${h.link_to("Back to {}".format(model_title_plural), index_url)}</li>
|
<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)):
|
% 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>
|
<li>${h.link_to("Edit this {}".format(model_title), action_url('edit', instance))}</li>
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
% endfor
|
% endfor
|
||||||
% if grid.show_actions_column:
|
% if grid.show_actions_column:
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
${grid.render_actions(row)}
|
${grid.render_actions(row, i)}
|
||||||
</td>
|
</td>
|
||||||
% endif
|
% endif
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -64,6 +64,9 @@ class MasterView(View):
|
||||||
row_attrs = {}
|
row_attrs = {}
|
||||||
cell_attrs = {}
|
cell_attrs = {}
|
||||||
|
|
||||||
|
grid_index = None
|
||||||
|
use_index_links = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Session(self):
|
def Session(self):
|
||||||
"""
|
"""
|
||||||
|
@ -93,10 +96,16 @@ class MasterView(View):
|
||||||
if self.request.GET.get('reset-to-default-filters') == 'true':
|
if self.request.GET.get('reset-to-default-filters') == 'true':
|
||||||
return self.redirect(self.request.current_route_url(_query=None))
|
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'):
|
if self.request.params.get('partial'):
|
||||||
self.request.response.content_type = b'text/html'
|
self.request.response.content_type = b'text/html'
|
||||||
self.request.response.text = grid.render_grid()
|
self.request.response.text = grid.render_grid()
|
||||||
return self.request.response
|
return self.request.response
|
||||||
|
|
||||||
return self.render_to_response('index', {'grid': grid})
|
return self.render_to_response('index', {'grid': grid})
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
|
@ -122,11 +131,12 @@ class MasterView(View):
|
||||||
def redirect_after_create(self, instance):
|
def redirect_after_create(self, instance):
|
||||||
return self.redirect(self.get_action_url('view', 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.
|
View for viewing details of an existing model record.
|
||||||
"""
|
"""
|
||||||
self.viewing = True
|
self.viewing = True
|
||||||
|
if instance is None:
|
||||||
instance = self.get_instance()
|
instance = self.get_instance()
|
||||||
form = self.make_form(instance)
|
form = self.make_form(instance)
|
||||||
return self.render_to_response('view', {
|
return self.render_to_response('view', {
|
||||||
|
@ -136,6 +146,28 @@ class MasterView(View):
|
||||||
'instance_deletable': self.deletable_instance(instance),
|
'instance_deletable': self.deletable_instance(instance),
|
||||||
'form': form})
|
'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):
|
def edit(self):
|
||||||
"""
|
"""
|
||||||
View for editing an existing model record.
|
View for editing an existing model record.
|
||||||
|
@ -327,7 +359,10 @@ class MasterView(View):
|
||||||
'index_title': self.get_index_title(),
|
'index_title': self.get_index_title(),
|
||||||
'index_url': self.get_index_url(),
|
'index_url': self.get_index_url(),
|
||||||
'action_url': self.get_action_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(data)
|
||||||
context.update(self.template_kwargs(**context))
|
context.update(self.template_kwargs(**context))
|
||||||
if hasattr(self, 'template_kwargs_{}'.format(template)):
|
if hasattr(self, 'template_kwargs_{}'.format(template)):
|
||||||
|
@ -445,9 +480,14 @@ class MasterView(View):
|
||||||
actions = []
|
actions = []
|
||||||
prefix = self.get_permission_prefix()
|
prefix = self.get_permission_prefix()
|
||||||
if self.viewable and self.request.has_perm('{}.view'.format(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
|
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):
|
def get_more_actions(self):
|
||||||
"""
|
"""
|
||||||
Return a list of 'more' actions for the grid.
|
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))
|
actions.append(self.make_action('delete', icon='trash', url=self.default_delete_url))
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def default_delete_url(self, row):
|
def default_delete_url(self, row, i=None):
|
||||||
if self.deletable_instance(row):
|
if self.deletable_instance(row):
|
||||||
return self.request.route_url('{}.delete'.format(self.get_route_prefix()),
|
return self.request.route_url('{}.delete'.format(self.get_route_prefix()),
|
||||||
**self.get_action_route_kwargs(row))
|
**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.
|
Make a new :class:`GridAction` instance for the current grid.
|
||||||
"""
|
"""
|
||||||
kwargs.setdefault('url', lambda r: self.request.route_url(
|
if url is None:
|
||||||
'{0}.{1}'.format(self.get_route_prefix(), key),
|
route = '{}.{}'.format(self.get_route_prefix(), key)
|
||||||
**self.get_action_route_kwargs(r)))
|
url = lambda r, i: self.request.route_url(route, **self.get_action_route_kwargs(r))
|
||||||
return GridAction(key, **kwargs)
|
return GridAction(key, url=url, **kwargs)
|
||||||
|
|
||||||
def get_action_route_kwargs(self, row):
|
def get_action_route_kwargs(self, row):
|
||||||
"""
|
"""
|
||||||
|
@ -542,16 +582,21 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
return session.query(self.get_model_class())
|
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
|
grid, filtered and sorted to match what would show on the UI, but not
|
||||||
paged etc.
|
paged etc.
|
||||||
"""
|
"""
|
||||||
|
if session is None:
|
||||||
|
session = self.Session()
|
||||||
grid = self.make_grid(session=session, pageable=False,
|
grid = self.make_grid(session=session, pageable=False,
|
||||||
main_actions=[], more_actions=[])
|
main_actions=[], more_actions=[])
|
||||||
return grid._fa_grid.rows
|
return grid._fa_grid.rows
|
||||||
|
|
||||||
|
def get_effective_query(self, session):
|
||||||
|
return self.get_effective_data(session)
|
||||||
|
|
||||||
def checkbox(self, instance):
|
def checkbox(self, instance):
|
||||||
"""
|
"""
|
||||||
Returns a boolean indicating whether ot not a checkbox should be
|
Returns a boolean indicating whether ot not a checkbox should be
|
||||||
|
@ -738,11 +783,18 @@ class MasterView(View):
|
||||||
|
|
||||||
# view
|
# view
|
||||||
if cls.viewable:
|
if cls.viewable:
|
||||||
config.add_route('{0}.view'.format(route_prefix), '{0}/{{{1}}}'.format(url_prefix, model_key))
|
config.add_tailbone_permission(permission_prefix, '{}.view'.format(permission_prefix),
|
||||||
config.add_view(cls, attr='view', route_name='{0}.view'.format(route_prefix),
|
"View {} details".format(model_title))
|
||||||
permission='{0}.view'.format(permission_prefix))
|
|
||||||
config.add_tailbone_permission(permission_prefix, '{0}.view'.format(permission_prefix),
|
# view by grid index
|
||||||
"View {0} Details".format(model_title))
|
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
|
# edit
|
||||||
if cls.editable:
|
if cls.editable:
|
||||||
|
|
Loading…
Reference in a new issue