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)
|
||||
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue