Improve merge support for records with no uuid
for now we "pretend" they have a uuid still, custom view is responsible for determining the value for each row if needed
This commit is contained in:
parent
58354e7adf
commit
214f3d9b1e
|
@ -170,6 +170,21 @@ class Grid(object):
|
||||||
'myfield': myrender,
|
'myfield': myrender,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.. attribute row_uuid_getter::
|
||||||
|
|
||||||
|
Optional callable to obtain the "UUID" (sic) value for each
|
||||||
|
data row. The default assumption as that each row object has a
|
||||||
|
``uuid`` attribute, but when that isn't the case, *and* the
|
||||||
|
grid needs to support checkboxes, we must "pretend" by
|
||||||
|
injecting some custom value to the ``uuid`` of the row data.
|
||||||
|
|
||||||
|
If necssary, set this to a callable like so::
|
||||||
|
|
||||||
|
def fake_uuid(row):
|
||||||
|
return row.some_custom_key
|
||||||
|
|
||||||
|
grid.row_uuid_getter = fake_uuid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, key, data, columns=None, width='auto', request=None,
|
def __init__(self, key, data, columns=None, width='auto', request=None,
|
||||||
|
@ -182,7 +197,7 @@ class Grid(object):
|
||||||
sortable=False, sorters={}, default_sortkey=None, default_sortdir='asc',
|
sortable=False, sorters={}, default_sortkey=None, default_sortdir='asc',
|
||||||
pageable=False, default_pagesize=None, default_page=1,
|
pageable=False, default_pagesize=None, default_page=1,
|
||||||
checkboxes=False, checked=None, check_handler=None, check_all_handler=None,
|
checkboxes=False, checked=None, check_handler=None, check_all_handler=None,
|
||||||
checkable=None,
|
checkable=None, row_uuid_getter=None,
|
||||||
clicking_row_checks_box=False, click_handlers=None,
|
clicking_row_checks_box=False, click_handlers=None,
|
||||||
main_actions=[], more_actions=[], delete_speedbump=False,
|
main_actions=[], more_actions=[], delete_speedbump=False,
|
||||||
ajax_data_url=None, component='tailbone-grid',
|
ajax_data_url=None, component='tailbone-grid',
|
||||||
|
@ -243,6 +258,7 @@ class Grid(object):
|
||||||
self.check_handler = check_handler
|
self.check_handler = check_handler
|
||||||
self.check_all_handler = check_all_handler
|
self.check_all_handler = check_all_handler
|
||||||
self.checkable = checkable
|
self.checkable = checkable
|
||||||
|
self.row_uuid_getter = row_uuid_getter
|
||||||
self.clicking_row_checks_box = clicking_row_checks_box
|
self.clicking_row_checks_box = clicking_row_checks_box
|
||||||
|
|
||||||
self.click_handlers = click_handlers or {}
|
self.click_handlers = click_handlers or {}
|
||||||
|
@ -1425,6 +1441,16 @@ class Grid(object):
|
||||||
})
|
})
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
def get_uuid_for_row(self, rowobj):
|
||||||
|
|
||||||
|
# use custom getter if set
|
||||||
|
if self.row_uuid_getter:
|
||||||
|
return self.row_uuid_getter(rowobj)
|
||||||
|
|
||||||
|
# otherwise fallback to normal uuid, if present
|
||||||
|
if hasattr(rowobj, 'uuid'):
|
||||||
|
return rowobj.uuid
|
||||||
|
|
||||||
def get_buefy_data(self):
|
def get_buefy_data(self):
|
||||||
"""
|
"""
|
||||||
Returns a list of data rows for the grid, for use with Buefy table.
|
Returns a list of data rows for the grid, for use with Buefy table.
|
||||||
|
@ -1481,8 +1507,9 @@ class Grid(object):
|
||||||
|
|
||||||
# maybe add UUID for convenience
|
# maybe add UUID for convenience
|
||||||
if 'uuid' not in self.columns:
|
if 'uuid' not in self.columns:
|
||||||
if hasattr(rowobj, 'uuid'):
|
uuid = self.get_uuid_for_row(rowobj)
|
||||||
row['uuid'] = rowobj.uuid
|
if uuid:
|
||||||
|
row['uuid'] = uuid
|
||||||
|
|
||||||
# set action URL(s) for row, as needed
|
# set action URL(s) for row, as needed
|
||||||
self.set_action_urls(row, rowobj, i)
|
self.set_action_urls(row, rowobj, i)
|
||||||
|
|
|
@ -192,7 +192,7 @@
|
||||||
% if grid.check_all_handler:
|
% if grid.check_all_handler:
|
||||||
@check-all="${grid.check_all_handler}"
|
@check-all="${grid.check_all_handler}"
|
||||||
% endif
|
% endif
|
||||||
% if isinstance(grid.checkable, six.string_types):
|
% if isinstance(grid.checkable, str):
|
||||||
:is-row-checkable="${grid.row_checkable}"
|
:is-row-checkable="${grid.row_checkable}"
|
||||||
% elif grid.checkable:
|
% elif grid.checkable:
|
||||||
:is-row-checkable="row => row._checkable"
|
:is-row-checkable="row => row._checkable"
|
||||||
|
|
|
@ -123,7 +123,7 @@
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
${h.form(request.current_route_url(), **{'@submit': 'submitSwapForm'})}
|
${h.form(request.current_route_url(), **{'@submit': 'submitSwapForm'})}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
${h.hidden('uuids', value='{},{}'.format(object_to_keep.uuid, object_to_remove.uuid))}
|
${h.hidden('uuids', value=f'{keeping_uuid},{removing_uuid}')}
|
||||||
<b-button native-type="submit"
|
<b-button native-type="submit"
|
||||||
:disabled="swapFormSubmitting">
|
:disabled="swapFormSubmitting">
|
||||||
{{ swapFormButtonText }}
|
{{ swapFormButtonText }}
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
${h.form(request.current_route_url(), **{'@submit': 'submitMergeForm'})}
|
${h.form(request.current_route_url(), **{'@submit': 'submitMergeForm'})}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
${h.hidden('uuids', value='{},{}'.format(object_to_remove.uuid, object_to_keep.uuid))}
|
${h.hidden('uuids', value=f'{removing_uuid},{keeping_uuid}')}
|
||||||
${h.hidden('commit-merge', value='yes')}
|
${h.hidden('commit-merge', value='yes')}
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
native-type="submit"
|
native-type="submit"
|
||||||
|
|
|
@ -441,6 +441,7 @@ class MasterView(View):
|
||||||
'checkable': self.checkbox,
|
'checkable': self.checkbox,
|
||||||
'clicking_row_checks_box': self.clicking_row_checks_box,
|
'clicking_row_checks_box': self.clicking_row_checks_box,
|
||||||
'assume_local_times': self.has_local_times,
|
'assume_local_times': self.has_local_times,
|
||||||
|
'row_uuid_getter': self.get_uuid_for_grid_row,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.sortable or self.pageable or self.filterable:
|
if self.sortable or self.pageable or self.filterable:
|
||||||
|
@ -453,6 +454,16 @@ class MasterView(View):
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
|
def get_uuid_for_grid_row(self, obj):
|
||||||
|
"""
|
||||||
|
If possible, this should return a "UUID" value to uniquely
|
||||||
|
identify the given object. Default of course is to use the
|
||||||
|
actual ``uuid`` attribute of the object, if present. This
|
||||||
|
value is needed by grids when checkboxes are used.
|
||||||
|
"""
|
||||||
|
if hasattr(obj, 'uuid'):
|
||||||
|
return obj.uuid
|
||||||
|
|
||||||
def configure_grid(self, grid):
|
def configure_grid(self, grid):
|
||||||
"""
|
"""
|
||||||
Perform "final" configuration for the main data grid.
|
Perform "final" configuration for the main data grid.
|
||||||
|
@ -2046,17 +2057,31 @@ class MasterView(View):
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def get_merge_objects(self):
|
||||||
|
"""
|
||||||
|
Must return 2 objects, obtained somehow from the request,
|
||||||
|
which are to be (potentially) merged.
|
||||||
|
|
||||||
|
:returns: 2-tuple of ``(object_to_remove, object_to_keep)``,
|
||||||
|
or ``None``.
|
||||||
|
"""
|
||||||
|
uuids = self.request.POST.get('uuids', '').split(',')
|
||||||
|
if len(uuids) == 2:
|
||||||
|
cls = self.get_model_class()
|
||||||
|
object_to_remove = self.Session.get(cls, uuids[0])
|
||||||
|
object_to_keep = self.Session.get(cls, uuids[1])
|
||||||
|
if object_to_remove and object_to_keep:
|
||||||
|
return object_to_remove, object_to_keep
|
||||||
|
|
||||||
def merge(self):
|
def merge(self):
|
||||||
"""
|
"""
|
||||||
Preview and execute a merge of two records.
|
Preview and execute a merge of two records.
|
||||||
"""
|
"""
|
||||||
object_to_remove = object_to_keep = None
|
object_to_remove = object_to_keep = None
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
uuids = self.request.POST.get('uuids', '').split(',')
|
objects = self.get_merge_objects()
|
||||||
if len(uuids) == 2:
|
if objects:
|
||||||
object_to_remove = self.Session.get(self.get_model_class(), uuids[0])
|
object_to_remove, object_to_keep = objects
|
||||||
object_to_keep = self.Session.get(self.get_model_class(), uuids[1])
|
|
||||||
|
|
||||||
if object_to_remove and object_to_keep and self.request.POST.get('commit-merge') == 'yes':
|
if object_to_remove and object_to_keep and self.request.POST.get('commit-merge') == 'yes':
|
||||||
msg = str(object_to_remove)
|
msg = str(object_to_remove)
|
||||||
try:
|
try:
|
||||||
|
@ -2073,13 +2098,17 @@ class MasterView(View):
|
||||||
|
|
||||||
remove = self.get_merge_data(object_to_remove)
|
remove = self.get_merge_data(object_to_remove)
|
||||||
keep = self.get_merge_data(object_to_keep)
|
keep = self.get_merge_data(object_to_keep)
|
||||||
return self.render_to_response('merge', {'object_to_remove': object_to_remove,
|
return self.render_to_response('merge', {
|
||||||
'object_to_keep': object_to_keep,
|
'object_to_remove': object_to_remove,
|
||||||
'view_url': lambda obj: self.get_action_url('view', obj),
|
'object_to_keep': object_to_keep,
|
||||||
'merge_fields': self.get_merge_fields(),
|
'removing_uuid': self.get_uuid_for_grid_row(object_to_remove),
|
||||||
'remove_data': remove,
|
'keeping_uuid': self.get_uuid_for_grid_row(object_to_keep),
|
||||||
'keep_data': keep,
|
'view_url': lambda obj: self.get_action_url('view', obj),
|
||||||
'resulting_data': self.get_merge_resulting_data(remove, keep)})
|
'merge_fields': self.get_merge_fields(),
|
||||||
|
'remove_data': remove,
|
||||||
|
'keep_data': keep,
|
||||||
|
'resulting_data': self.get_merge_resulting_data(remove, keep),
|
||||||
|
})
|
||||||
|
|
||||||
def validate_merge(self, removing, keeping):
|
def validate_merge(self, removing, keeping):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue