Add back-end support for multi-column grid sorting
or very nearly, anyway. front-end still just supports 1 column yet
This commit is contained in:
parent
4beca7af20
commit
6d7754cf2a
|
@ -33,13 +33,7 @@ from cornice import resource, Service
|
|||
|
||||
from tailbone.api import APIView, api
|
||||
from tailbone.db import Session
|
||||
|
||||
|
||||
class SortColumn(object):
|
||||
|
||||
def __init__(self, field_name, model_name=None):
|
||||
self.field_name = field_name
|
||||
self.model_name = model_name
|
||||
from tailbone.util import SortColumn
|
||||
|
||||
|
||||
class APIMasterView(APIView):
|
||||
|
|
|
@ -24,12 +24,13 @@
|
|||
Core Grid Classes
|
||||
"""
|
||||
|
||||
from urllib.parse import urlencode
|
||||
import warnings
|
||||
import logging
|
||||
|
||||
from six.moves import urllib
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
from sa_filters import apply_sort
|
||||
|
||||
from rattail.db.types import GPCType
|
||||
from rattail.util import prettify, pretty_boolean, pretty_quantity
|
||||
|
@ -552,48 +553,6 @@ class Grid(object):
|
|||
return self.url(obj)
|
||||
return self.url
|
||||
|
||||
def make_webhelpers_grid(self):
|
||||
kwargs = dict(self._whgrid_kwargs)
|
||||
kwargs['request'] = self.request
|
||||
kwargs['url'] = self.make_url
|
||||
|
||||
columns = list(self.columns)
|
||||
column_labels = kwargs.setdefault('column_labels', {})
|
||||
column_formats = kwargs.setdefault('column_formats', {})
|
||||
|
||||
for key, value in self.labels.items():
|
||||
column_labels.setdefault(key, value)
|
||||
|
||||
if self.checkboxes:
|
||||
columns.insert(0, 'checkbox')
|
||||
column_labels['checkbox'] = tags.checkbox('check-all')
|
||||
column_formats['checkbox'] = self.checkbox_column_format
|
||||
|
||||
if self.renderers:
|
||||
kwargs['renderers'] = self.renderers
|
||||
if self.extra_row_class:
|
||||
kwargs['extra_record_class'] = self.extra_row_class
|
||||
if self.linked_columns:
|
||||
kwargs['linked_columns'] = list(self.linked_columns)
|
||||
|
||||
if self.main_actions or self.more_actions:
|
||||
columns.append('actions')
|
||||
column_formats['actions'] = self.actions_column_format
|
||||
|
||||
# TODO: pretty sure this factory doesn't serve all use cases yet?
|
||||
factory = CustomWebhelpersGrid
|
||||
# factory = webhelpers2_grid.Grid
|
||||
if self.sortable:
|
||||
# factory = CustomWebhelpersGrid
|
||||
kwargs['order_column'] = self.sortkey
|
||||
kwargs['order_direction'] = 'dsc' if self.sortdir == 'desc' else 'asc'
|
||||
|
||||
grid = factory(self.make_visible_data(), columns, **kwargs)
|
||||
if self.sortable:
|
||||
grid.exclude_ordering = list([key for key in grid.exclude_ordering
|
||||
if key not in self.sorters])
|
||||
return grid
|
||||
|
||||
def make_default_renderers(self, renderers):
|
||||
"""
|
||||
Make the default set of column renderers for the grid.
|
||||
|
@ -638,19 +597,6 @@ class Grid(object):
|
|||
def actions_column_format(self, column_number, row_number, item):
|
||||
return HTML.td(self.render_actions(item, row_number), class_='actions')
|
||||
|
||||
def render_grid(self, template='/grids/grid.mako', **kwargs):
|
||||
context = kwargs
|
||||
context['grid'] = self
|
||||
context['request'] = self.request
|
||||
grid_class = ''
|
||||
if self.width == 'full':
|
||||
grid_class = 'full'
|
||||
elif self.width == 'half':
|
||||
grid_class = 'half'
|
||||
context['grid_class'] = '{} {}'.format(grid_class, context.get('grid_class', ''))
|
||||
context.setdefault('grid_attrs', {})
|
||||
return render(template, context)
|
||||
|
||||
def get_default_filters(self):
|
||||
"""
|
||||
Returns the default set of filters provided by the grid.
|
||||
|
@ -761,6 +707,9 @@ class Grid(object):
|
|||
return query
|
||||
return query.order_by(getattr(column, direction)())
|
||||
|
||||
sorter._class = class_
|
||||
sorter._column = column
|
||||
|
||||
return sorter
|
||||
|
||||
def make_simple_sorter(self, key, foldcase=False):
|
||||
|
@ -801,8 +750,12 @@ class Grid(object):
|
|||
# initial default settings
|
||||
settings = {}
|
||||
if self.sortable:
|
||||
settings['sortkey'] = self.default_sortkey
|
||||
settings['sortdir'] = self.default_sortdir
|
||||
if self.default_sortkey:
|
||||
settings['sorters.length'] = 1
|
||||
settings['sorters.1.key'] = self.default_sortkey
|
||||
settings['sorters.1.dir'] = self.default_sortdir
|
||||
else:
|
||||
settings['sorters.length'] = 0
|
||||
if self.pageable:
|
||||
settings['pagesize'] = self.get_default_pagesize()
|
||||
settings['page'] = self.default_page
|
||||
|
@ -875,8 +828,12 @@ class Grid(object):
|
|||
filtr.verb = settings['filter.{}.verb'.format(filtr.key)]
|
||||
filtr.value = settings['filter.{}.value'.format(filtr.key)]
|
||||
if self.sortable:
|
||||
self.sortkey = settings['sortkey']
|
||||
self.sortdir = settings['sortdir']
|
||||
self.active_sorters = []
|
||||
for i in range(1, settings['sorters.length'] + 1):
|
||||
self.active_sorters.append((
|
||||
settings[f'sorters.{i}.key'],
|
||||
settings[f'sorters.{i}.dir'],
|
||||
))
|
||||
if self.pageable:
|
||||
self.pagesize = settings['pagesize']
|
||||
self.page = settings['page']
|
||||
|
@ -895,21 +852,36 @@ class Grid(object):
|
|||
# anything...
|
||||
session = Session()
|
||||
if user not in session:
|
||||
user = session.merge(user)
|
||||
# TODO: pretty sure there is no need to *merge* here..
|
||||
# but we shall see if any breakage happens maybe
|
||||
#user = session.merge(user)
|
||||
user = session.get(user.__class__, user.uuid)
|
||||
|
||||
# User defaults should have all or nothing, so just check one key.
|
||||
key = 'tailbone.{}.grid.{}.sortkey'.format(user.uuid, self.key)
|
||||
app = self.request.rattail_config.get_app()
|
||||
return app.get_setting(Session(), key) is not None
|
||||
|
||||
# user defaults should be all or nothing, so just check one key
|
||||
key = f'tailbone.{user.uuid}.grid.{self.key}.sorters.length'
|
||||
if app.get_setting(session, key) is not None:
|
||||
return True
|
||||
|
||||
# TODO: this is deprecated but should work its way out of the
|
||||
# system in a little while (?)..then can remove this entirely
|
||||
key = f'tailbone.{user.uuid}.grid.{self.key}.sortkey'
|
||||
if app.get_setting(session, key) is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def apply_user_defaults(self, settings):
|
||||
"""
|
||||
Update the given settings dict with user defaults, if any exist.
|
||||
"""
|
||||
app = self.request.rattail_config.get_app()
|
||||
session = Session()
|
||||
prefix = f'tailbone.{self.request.user.uuid}.grid.{self.key}'
|
||||
|
||||
def merge(key, normalize=lambda v: v):
|
||||
skey = 'tailbone.{}.grid.{}.{}'.format(self.request.user.uuid, self.key, key)
|
||||
app = self.request.rattail_config.get_app()
|
||||
value = app.get_setting(Session(), skey)
|
||||
value = app.get_setting(session, f'{prefix}.{key}')
|
||||
settings[key] = normalize(value)
|
||||
|
||||
if self.filterable:
|
||||
|
@ -919,8 +891,52 @@ class Grid(object):
|
|||
merge('filter.{}.value'.format(filtr.key))
|
||||
|
||||
if self.sortable:
|
||||
merge('sortkey')
|
||||
merge('sortdir')
|
||||
|
||||
# first clear existing settings for *sorting* only
|
||||
# nb. this is because number of sort settings will vary
|
||||
for key in list(settings):
|
||||
if key.startswith('sorters.'):
|
||||
del settings[key]
|
||||
|
||||
# check for *deprecated* settings, and use those if present
|
||||
# TODO: obviously should stop this, but must wait until
|
||||
# all old settings have been flushed out. which in the
|
||||
# case of user-persisted settings, could be a while...
|
||||
sortkey = app.get_setting(session, f'{prefix}.sortkey')
|
||||
if sortkey:
|
||||
settings['sorters.length'] = 1
|
||||
settings['sorters.1.key'] = sortkey
|
||||
settings['sorters.1.dir'] = app.get_setting(session, f'{prefix}.sortdir')
|
||||
|
||||
# nb. re-persist these user settings per new
|
||||
# convention, so deprecated settings go away and we
|
||||
# can remove this logic after a while..
|
||||
app = self.request.rattail_config.get_app()
|
||||
model = app.model
|
||||
prefix = f'tailbone.{self.request.user.uuid}.grid.{self.key}'
|
||||
query = Session.query(model.Setting)\
|
||||
.filter(sa.or_(
|
||||
model.Setting.name.like(f'{prefix}.sorters.%'),
|
||||
model.Setting.name == f'{prefix}.sortkey',
|
||||
model.Setting.name == f'{prefix}.sortdir'))
|
||||
for setting in query.all():
|
||||
Session.delete(setting)
|
||||
Session.flush()
|
||||
|
||||
def persist(key):
|
||||
app.save_setting(Session(),
|
||||
f'tailbone.{self.request.user.uuid}.grid.{self.key}.{key}',
|
||||
settings[key])
|
||||
|
||||
persist('sorters.length')
|
||||
persist('sorters.1.key')
|
||||
persist('sorters.1.dir')
|
||||
|
||||
else: # the future
|
||||
merge('sorters.length', int)
|
||||
for i in range(1, settings['sorters.length'] + 1):
|
||||
merge(f'sorters.{i}.key')
|
||||
merge(f'sorters.{i}.dir')
|
||||
|
||||
if self.pageable:
|
||||
merge('pagesize', int)
|
||||
|
@ -939,10 +955,16 @@ class Grid(object):
|
|||
return True
|
||||
|
||||
elif type_ == 'sort':
|
||||
|
||||
# TODO: remove this eventually, but some links in the wild
|
||||
# may still include these params, so leave it for now
|
||||
for key in ['sortkey', 'sortdir']:
|
||||
if key in self.request.GET:
|
||||
return True
|
||||
|
||||
if 'sort1key' in self.request.GET:
|
||||
return True
|
||||
|
||||
elif type_ == 'page':
|
||||
for key in ['pagesize', 'page']:
|
||||
if key in self.request.GET:
|
||||
|
@ -956,10 +978,12 @@ class Grid(object):
|
|||
"""
|
||||
# session should have all or nothing, so just check a few keys which
|
||||
# should be guaranteed present if anything has been stashed
|
||||
for key in ['page', 'sortkey']:
|
||||
if 'grid.{}.{}'.format(self.key, key) in self.request.session:
|
||||
prefix = f'grid.{self.key}'
|
||||
for key in ['page', 'sorters.length']:
|
||||
if f'{prefix}.{key}' in self.request.session:
|
||||
return True
|
||||
return any([key.startswith('grid.{}.filter'.format(self.key)) for key in self.request.session])
|
||||
return any([key.startswith(f'{prefix}.filter')
|
||||
for key in self.request.session])
|
||||
|
||||
def get_setting(self, source, settings, key, normalize=lambda v: v, default=None):
|
||||
"""
|
||||
|
@ -1044,8 +1068,46 @@ class Grid(object):
|
|||
"""
|
||||
if not self.sortable:
|
||||
return
|
||||
settings['sortkey'] = self.get_setting(source, settings, 'sortkey')
|
||||
settings['sortdir'] = self.get_setting(source, settings, 'sortdir')
|
||||
|
||||
if source == 'request':
|
||||
|
||||
# TODO: remove this eventually, but some links in the wild
|
||||
# may still include these params, so leave it for now
|
||||
if 'sortkey' in self.request.GET:
|
||||
settings['sorters.length'] = 1
|
||||
settings['sorters.1.key'] = self.get_setting(source, settings, 'sortkey')
|
||||
settings['sorters.1.dir'] = self.get_setting(source, settings, 'sortdir')
|
||||
|
||||
else: # the future
|
||||
i = 1
|
||||
while True:
|
||||
skey = f'sort{i}key'
|
||||
if skey in self.request.GET:
|
||||
settings[f'sorters.{i}.key'] = self.get_setting(source, settings, skey)
|
||||
settings[f'sorters.{i}.dir'] = self.get_setting(source, settings, f'sort{i}dir')
|
||||
else:
|
||||
break
|
||||
i += 1
|
||||
settings['sorters.length'] = i - 1
|
||||
|
||||
else: # session
|
||||
|
||||
# TODO: definitely will remove this, but leave it for now
|
||||
# so it doesn't monkey with current user sessions when
|
||||
# next upgrade happens. so, remove after all are upgraded
|
||||
sortkey = self.get_setting(source, settings, 'sortkey')
|
||||
if sortkey:
|
||||
settings['sorters.length'] = 1
|
||||
settings['sorters.1.key'] = sortkey
|
||||
settings['sorters.1.dir'] = self.get_setting(source, settings, 'sortdir')
|
||||
|
||||
else: # the future
|
||||
settings['sorters.length'] = self.get_setting(source, settings,
|
||||
'sorters.length', int)
|
||||
for i in range(1, settings['sorters.length'] + 1):
|
||||
for key in ('key', 'dir'):
|
||||
skey = f'sorters.{i}.{key}'
|
||||
settings[skey] = self.get_setting(source, settings, skey)
|
||||
|
||||
def update_page_settings(self, settings):
|
||||
"""
|
||||
|
@ -1100,8 +1162,40 @@ class Grid(object):
|
|||
persist('filter.{}.value'.format(filtr.key))
|
||||
|
||||
if self.sortable:
|
||||
persist('sortkey')
|
||||
persist('sortdir')
|
||||
|
||||
# first clear existing settings for *sorting* only
|
||||
# nb. this is because number of sort settings will vary
|
||||
if to == 'defaults':
|
||||
model = self.request.rattail_config.get_model()
|
||||
prefix = f'tailbone.{self.request.user.uuid}.grid.{self.key}'
|
||||
query = Session.query(model.Setting)\
|
||||
.filter(sa.or_(
|
||||
model.Setting.name.like(f'{prefix}.sorters.%'),
|
||||
# TODO: remove these eventually,
|
||||
# but probably should wait until
|
||||
# all nodes have been upgraded for
|
||||
# (quite) a while?
|
||||
model.Setting.name == f'{prefix}.sortkey',
|
||||
model.Setting.name == f'{prefix}.sortdir'))
|
||||
for setting in query.all():
|
||||
Session.delete(setting)
|
||||
Session.flush()
|
||||
else: # session
|
||||
prefix = f'grid.{self.key}'
|
||||
for key in list(self.request.session):
|
||||
if key.startswith(f'{prefix}.sorters.'):
|
||||
del self.request.session[key]
|
||||
# TODO: definitely will remove these, but leave for
|
||||
# now so they don't monkey with current user sessions
|
||||
# when next upgrade happens. so, remove after all are
|
||||
# upgraded
|
||||
self.request.session.pop(f'{prefix}.sortkey', None)
|
||||
self.request.session.pop(f'{prefix}.sortdir', None)
|
||||
|
||||
persist('sorters.length')
|
||||
for i in range(1, settings['sorters.length'] + 1):
|
||||
persist(f'sorters.{i}.key')
|
||||
persist(f'sorters.{i}.dir')
|
||||
|
||||
if self.pageable:
|
||||
persist('pagesize')
|
||||
|
@ -1131,21 +1225,32 @@ class Grid(object):
|
|||
"""
|
||||
Sort the given query according to current settings, and return the result.
|
||||
"""
|
||||
# Cannot sort unless we know which column to sort by.
|
||||
if not self.sortkey:
|
||||
# bail if no sort settings
|
||||
if not self.active_sorters:
|
||||
return data
|
||||
|
||||
# Cannot sort unless we have a sort function.
|
||||
sortfunc = self.sorters.get(self.sortkey)
|
||||
if not sortfunc:
|
||||
return data
|
||||
# convert sort settings into a 'sortspec' for use with sa-filters
|
||||
full_spec = []
|
||||
for sortkey, sortdir in self.active_sorters:
|
||||
sortfunc = self.sorters.get(sortkey)
|
||||
if sortfunc:
|
||||
spec = {
|
||||
'sortkey': sortkey,
|
||||
'model': sortfunc._class.__name__,
|
||||
'field': sortfunc._column.name,
|
||||
'direction': sortdir or 'asc',
|
||||
}
|
||||
# spec.sortkey = sortkey
|
||||
full_spec.append(spec)
|
||||
|
||||
# We can provide a default sort direction though.
|
||||
sortdir = getattr(self, 'sortdir', 'asc')
|
||||
if self.sortkey in self.joiners and self.sortkey not in self.joined:
|
||||
data = self.joiners[self.sortkey](data)
|
||||
self.joined.add(self.sortkey)
|
||||
return sortfunc(data, sortdir)
|
||||
# apply joins needed for this sort spec
|
||||
for spec in full_spec:
|
||||
sortkey = spec['sortkey']
|
||||
if sortkey in self.joiners and sortkey not in self.joined:
|
||||
data = self.joiners[sortkey](data)
|
||||
self.joined.add(sortkey)
|
||||
|
||||
return apply_sort(data, full_spec)
|
||||
|
||||
def paginate_data(self, data):
|
||||
"""
|
||||
|
@ -1197,7 +1302,7 @@ class Grid(object):
|
|||
data = self.pager
|
||||
return data
|
||||
|
||||
def render_complete(self, template='/grids/complete.mako', **kwargs):
|
||||
def render_complete(self, template='/grids/buefy.mako', **kwargs):
|
||||
"""
|
||||
Render the complete grid, including filters.
|
||||
"""
|
||||
|
@ -1717,5 +1822,5 @@ class URLMaker(object):
|
|||
params = self.request.GET.copy()
|
||||
params["page"] = page
|
||||
params["partial"] = "1"
|
||||
qs = urllib.parse.urlencode(params, True)
|
||||
qs = urlencode(params, True)
|
||||
return '{}?{}'.format(self.request.path, qs)
|
||||
|
|
|
@ -202,7 +202,7 @@
|
|||
% endif
|
||||
|
||||
% if grid.sortable:
|
||||
:default-sort="[sortField, sortOrder]"
|
||||
:default-sort="sortingPriority[0]"
|
||||
backend-sorting
|
||||
@sort="onSort"
|
||||
% endif
|
||||
|
@ -352,8 +352,9 @@
|
|||
firstItem: ${json.dumps(grid_data['first_item'] if grid.pageable else None)|n},
|
||||
lastItem: ${json.dumps(grid_data['last_item'] if grid.pageable else None)|n},
|
||||
|
||||
sortField: ${json.dumps(grid.sortkey if grid.sortable else None)|n},
|
||||
sortOrder: ${json.dumps(grid.sortdir if grid.sortable else None)|n},
|
||||
% if grid.sortable:
|
||||
sortingPriority: ${json.dumps(grid.active_sorters)|n},
|
||||
% endif
|
||||
|
||||
## filterable: ${json.dumps(grid.filterable)|n},
|
||||
filters: ${json.dumps(filters_data if grid.filterable else None)|n},
|
||||
|
@ -454,8 +455,10 @@
|
|||
getBasicParams() {
|
||||
let params = {}
|
||||
% if grid.sortable:
|
||||
params.sortkey = this.sortField
|
||||
params.sortdir = this.sortOrder
|
||||
for (let i = 1; i <= this.sortingPriority.length; i++) {
|
||||
params['sort'+i+'key'] = this.sortingPriority[i-1][0]
|
||||
params['sort'+i+'dir'] = this.sortingPriority[i-1][1]
|
||||
}
|
||||
% endif
|
||||
% if grid.pageable:
|
||||
params.pagesize = this.perPage
|
||||
|
@ -535,8 +538,7 @@
|
|||
},
|
||||
|
||||
onSort(field, order) {
|
||||
this.sortField = field
|
||||
this.sortOrder = order
|
||||
this.sortingPriority = [[field, order]]
|
||||
// always reset to first page when changing sort options
|
||||
// TODO: i mean..right? would we ever not want that?
|
||||
this.currentPage = 1
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
<div class="grid-wrapper">
|
||||
|
||||
<table class="grid-header">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
<td class="filters" rowspan="2">
|
||||
% if grid.filterable:
|
||||
${grid.render_filters(allow_save_defaults=allow_save_defaults)|n}
|
||||
% endif
|
||||
</td>
|
||||
|
||||
<td class="menu">
|
||||
% if context_menu:
|
||||
<ul id="context-menu">
|
||||
${context_menu|n}
|
||||
</ul>
|
||||
% endif
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="tools">
|
||||
% if tools:
|
||||
<div class="grid-tools">
|
||||
${tools|n}
|
||||
</div><!-- grid-tools -->
|
||||
% endif
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table><!-- grid-header -->
|
||||
|
||||
${grid.render_grid()|n}
|
||||
|
||||
</div><!-- grid-wrapper -->
|
|
@ -1,21 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<div class="grid ${grid_class}" data-delete-speedbump="${'true' if grid.delete_speedbump else 'false'}" ${h.HTML.render_attrs(grid_attrs)}>
|
||||
<table>
|
||||
${grid.make_webhelpers_grid()}
|
||||
</table>
|
||||
% if grid.pageable and grid.pager:
|
||||
<div class="pager">
|
||||
<p class="showing">
|
||||
${"showing {} thru {} of {:,d}".format(grid.pager.first_item, grid.pager.last_item, grid.pager.item_count)}
|
||||
% if grid.pager.page_count > 1:
|
||||
${"(page {} of {:,d})".format(grid.pager.page, grid.pager.page_count)}
|
||||
% endif
|
||||
</p>
|
||||
<p class="page-links">
|
||||
${h.select('pagesize', grid.pager.items_per_page, grid.get_pagesize_options())}
|
||||
per page
|
||||
${grid.pager.pager('$link_first $link_previous ~1~ $link_next $link_last', symbol_next='next', symbol_previous='prev')|n}
|
||||
</p>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
|
@ -44,6 +44,17 @@ from webhelpers2.html import HTML, tags
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SortColumn(object):
|
||||
"""
|
||||
Generic representation of a sort column, for use with sorting grid
|
||||
data as well as with API.
|
||||
"""
|
||||
|
||||
def __init__(self, field_name, model_name=None):
|
||||
self.field_name = field_name
|
||||
self.model_name = model_name
|
||||
|
||||
|
||||
def get_csrf_token(request):
|
||||
"""
|
||||
Convenience function to retrieve the effective CSRF token for the given
|
||||
|
|
|
@ -476,36 +476,6 @@ class CustomerView(MasterView):
|
|||
items.append(HTML.tag('li', c=[link]))
|
||||
return HTML.tag('ul', c=items)
|
||||
|
||||
# TODO: remove if no longer used
|
||||
def render_people_removable(self, customer, field):
|
||||
people = customer.people
|
||||
if not people:
|
||||
return ""
|
||||
|
||||
route_prefix = self.get_route_prefix()
|
||||
permission_prefix = self.get_permission_prefix()
|
||||
|
||||
view_url = lambda p, i: self.request.route_url('people.view', uuid=p.uuid)
|
||||
actions = [
|
||||
grids.GridAction('view', icon='zoomin', url=view_url),
|
||||
]
|
||||
if self.people_detachable and self.request.has_perm('{}.detach_person'.format(permission_prefix)):
|
||||
url = lambda p, i: self.request.route_url('{}.detach_person'.format(route_prefix),
|
||||
uuid=customer.uuid, person_uuid=p.uuid)
|
||||
actions.append(
|
||||
grids.GridAction('detach', icon='trash', url=url))
|
||||
|
||||
columns = ['first_name', 'last_name', 'display_name']
|
||||
g = grids.Grid(
|
||||
key='{}.people'.format(route_prefix),
|
||||
data=customer.people,
|
||||
columns=columns,
|
||||
labels={'display_name': "Full Name"},
|
||||
url=lambda p: self.request.route_url('people.view', uuid=p.uuid),
|
||||
linked_columns=columns,
|
||||
main_actions=actions)
|
||||
return HTML.literal(g.render_grid())
|
||||
|
||||
def render_shoppers(self, customer, field):
|
||||
route_prefix = self.get_route_prefix()
|
||||
permission_prefix = self.get_permission_prefix()
|
||||
|
|
|
@ -340,11 +340,9 @@ class MasterView(View):
|
|||
if grid.pageable and hasattr(grid, 'pager'):
|
||||
self.first_visible_grid_index = grid.pager.first_item
|
||||
|
||||
# return grid only, if partial page was requested
|
||||
# return grid data only, if partial page was requested
|
||||
if self.request.params.get('partial'):
|
||||
# render grid data only, as JSON
|
||||
return render_to_response('json', grid.get_buefy_data(),
|
||||
request=self.request)
|
||||
return self.json_response(grid.get_buefy_data())
|
||||
|
||||
context = {
|
||||
'grid': grid,
|
||||
|
@ -1156,8 +1154,7 @@ class MasterView(View):
|
|||
# return grid only, if partial page was requested
|
||||
if self.request.params.get('partial'):
|
||||
# render grid data only, as JSON
|
||||
return render_to_response('json', grid.get_buefy_data(),
|
||||
request=self.request)
|
||||
return self.json_response(grid.get_buefy_data())
|
||||
|
||||
context = {
|
||||
'instance': instance,
|
||||
|
@ -1284,8 +1281,7 @@ class MasterView(View):
|
|||
# return grid only, if partial page was requested
|
||||
if self.request.params.get('partial'):
|
||||
# render grid data only, as JSON
|
||||
return render_to_response('json', grid.get_buefy_data(),
|
||||
request=self.request)
|
||||
return self.json_response(grid.get_buefy_data())
|
||||
|
||||
return self.render_to_response('versions', {
|
||||
'instance': instance,
|
||||
|
|
|
@ -461,7 +461,8 @@ class MemberEquityPaymentView(MasterView):
|
|||
g.set_renderer(field, self.render_member_key)
|
||||
g.set_filter(field, attr,
|
||||
label=self.get_member_key_label(),
|
||||
default_active=True)
|
||||
default_active=True,
|
||||
default_verb='equal')
|
||||
g.set_sorter(field, attr)
|
||||
|
||||
# member (name)
|
||||
|
|
Loading…
Reference in a new issue