feat: move single-column grid sorting logic to wuttaweb
This commit is contained in:
parent
c95e42bf82
commit
ec36df4a34
7 changed files with 475 additions and 200 deletions
|
@ -1,6 +1,8 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from sqlalchemy import orm
|
||||
|
||||
from tailbone.grids import core as mod
|
||||
from tests.util import WebTestCase
|
||||
|
@ -27,6 +29,16 @@ class TestGrid(WebTestCase):
|
|||
grid = self.make_grid(component='blarg')
|
||||
self.assertEqual(grid.vue_tagname, 'blarg')
|
||||
|
||||
# default_sortkey, default_sortdir
|
||||
grid = self.make_grid()
|
||||
self.assertEqual(grid.sort_defaults, [])
|
||||
grid = self.make_grid(default_sortkey='name')
|
||||
self.assertEqual(grid.sort_defaults, [mod.SortInfo('name', 'asc')])
|
||||
grid = self.make_grid(default_sortdir='desc')
|
||||
self.assertEqual(grid.sort_defaults, [])
|
||||
grid = self.make_grid(default_sortkey='name', default_sortdir='desc')
|
||||
self.assertEqual(grid.sort_defaults, [mod.SortInfo('name', 'desc')])
|
||||
|
||||
# pageable
|
||||
grid = self.make_grid()
|
||||
self.assertFalse(grid.paginated)
|
||||
|
@ -159,6 +171,27 @@ class TestGrid(WebTestCase):
|
|||
grid.set_action_urls(setting, setting, 0)
|
||||
self.assertEqual(setting['_action_url_view'], '/blarg')
|
||||
|
||||
def test_default_sortkey(self):
|
||||
grid = self.make_grid()
|
||||
self.assertEqual(grid.sort_defaults, [])
|
||||
self.assertIsNone(grid.default_sortkey)
|
||||
grid.default_sortkey = 'name'
|
||||
self.assertEqual(grid.sort_defaults, [mod.SortInfo('name', 'asc')])
|
||||
self.assertEqual(grid.default_sortkey, 'name')
|
||||
grid.default_sortkey = 'value'
|
||||
self.assertEqual(grid.sort_defaults, [mod.SortInfo('value', 'asc')])
|
||||
self.assertEqual(grid.default_sortkey, 'value')
|
||||
|
||||
def test_default_sortdir(self):
|
||||
grid = self.make_grid()
|
||||
self.assertEqual(grid.sort_defaults, [])
|
||||
self.assertIsNone(grid.default_sortdir)
|
||||
self.assertRaises(ValueError, setattr, grid, 'default_sortdir', 'asc')
|
||||
grid.sort_defaults = [mod.SortInfo('name', 'asc')]
|
||||
grid.default_sortdir = 'desc'
|
||||
self.assertEqual(grid.sort_defaults, [mod.SortInfo('name', 'desc')])
|
||||
self.assertEqual(grid.default_sortdir, 'desc')
|
||||
|
||||
def test_pageable(self):
|
||||
grid = self.make_grid()
|
||||
self.assertFalse(grid.paginated)
|
||||
|
@ -219,6 +252,212 @@ class TestGrid(WebTestCase):
|
|||
size = grid.get_pagesize()
|
||||
self.assertEqual(size, 15)
|
||||
|
||||
def test_set_sorter(self):
|
||||
model = self.app.model
|
||||
grid = self.make_grid(model_class=model.Setting,
|
||||
sortable=True, sort_on_backend=True)
|
||||
|
||||
# passing None will remove sorter
|
||||
self.assertIn('name', grid.sorters)
|
||||
grid.set_sorter('name', None)
|
||||
self.assertNotIn('name', grid.sorters)
|
||||
|
||||
# can recreate sorter with just column name
|
||||
grid.set_sorter('name')
|
||||
self.assertIn('name', grid.sorters)
|
||||
grid.remove_sorter('name')
|
||||
self.assertNotIn('name', grid.sorters)
|
||||
grid.set_sorter('name', 'name')
|
||||
self.assertIn('name', grid.sorters)
|
||||
|
||||
# can recreate sorter with model property
|
||||
grid.remove_sorter('name')
|
||||
self.assertNotIn('name', grid.sorters)
|
||||
grid.set_sorter('name', model.Setting.name)
|
||||
self.assertIn('name', grid.sorters)
|
||||
|
||||
# extra kwargs are ignored
|
||||
grid.remove_sorter('name')
|
||||
self.assertNotIn('name', grid.sorters)
|
||||
grid.set_sorter('name', model.Setting.name, foo='bar')
|
||||
self.assertIn('name', grid.sorters)
|
||||
|
||||
# passing multiple args will invoke make_filter() directly
|
||||
grid.remove_sorter('name')
|
||||
self.assertNotIn('name', grid.sorters)
|
||||
with patch.object(grid, 'make_sorter') as make_sorter:
|
||||
make_sorter.return_value = 42
|
||||
grid.set_sorter('name', 'foo', 'bar')
|
||||
make_sorter.assert_called_once_with('foo', 'bar')
|
||||
self.assertEqual(grid.sorters['name'], 42)
|
||||
|
||||
def test_make_simple_sorter(self):
|
||||
model = self.app.model
|
||||
grid = self.make_grid(model_class=model.Setting,
|
||||
sortable=True, sort_on_backend=True)
|
||||
|
||||
# delegates to grid.make_sorter()
|
||||
with patch.object(grid, 'make_sorter') as make_sorter:
|
||||
make_sorter.return_value = 42
|
||||
sorter = grid.make_simple_sorter('name', foldcase=True)
|
||||
make_sorter.assert_called_once_with('name', foldcase=True)
|
||||
self.assertEqual(sorter, 42)
|
||||
|
||||
def test_load_settings(self):
|
||||
model = self.app.model
|
||||
|
||||
# nb. first use a paging grid
|
||||
grid = self.make_grid(key='foo', paginated=True, paginate_on_backend=True,
|
||||
pagesize=20, page=1)
|
||||
|
||||
# settings are loaded, applied, saved
|
||||
self.assertEqual(grid.page, 1)
|
||||
self.assertNotIn('grid.foo.page', self.request.session)
|
||||
self.request.GET = {'pagesize': '10', 'page': '2'}
|
||||
grid.load_settings()
|
||||
self.assertEqual(grid.page, 2)
|
||||
self.assertEqual(self.request.session['grid.foo.page'], 2)
|
||||
|
||||
# can skip the saving step
|
||||
self.request.GET = {'pagesize': '10', 'page': '3'}
|
||||
grid.load_settings(store=False)
|
||||
self.assertEqual(grid.page, 3)
|
||||
self.assertEqual(self.request.session['grid.foo.page'], 2)
|
||||
|
||||
# no error for non-paginated grid
|
||||
grid = self.make_grid(key='foo', paginated=False)
|
||||
grid.load_settings()
|
||||
self.assertFalse(grid.paginated)
|
||||
|
||||
# nb. next use a sorting grid
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
sortable=True, sort_on_backend=True)
|
||||
|
||||
# settings are loaded, applied, saved
|
||||
self.assertEqual(grid.sort_defaults, [])
|
||||
self.assertFalse(hasattr(grid, 'active_sorters'))
|
||||
self.request.GET = {'sort1key': 'name', 'sort1dir': 'desc'}
|
||||
grid.load_settings()
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'desc'}])
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.length'], 1)
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.1.key'], 'name')
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.1.dir'], 'desc')
|
||||
|
||||
# can skip the saving step
|
||||
self.request.GET = {'sort1key': 'name', 'sort1dir': 'asc'}
|
||||
grid.load_settings(store=False)
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'asc'}])
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.length'], 1)
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.1.key'], 'name')
|
||||
self.assertEqual(self.request.session['grid.settings.sorters.1.dir'], 'desc')
|
||||
|
||||
# no error for non-sortable grid
|
||||
grid = self.make_grid(key='foo', sortable=False)
|
||||
grid.load_settings()
|
||||
self.assertFalse(grid.sortable)
|
||||
|
||||
# with sort defaults
|
||||
grid = self.make_grid(model_class=model.Setting, sortable=True,
|
||||
sort_on_backend=True, sort_defaults='name')
|
||||
self.assertFalse(hasattr(grid, 'active_sorters'))
|
||||
grid.load_settings()
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'asc'}])
|
||||
|
||||
# with multi-column sort defaults
|
||||
grid = self.make_grid(model_class=model.Setting, sortable=True,
|
||||
sort_on_backend=True)
|
||||
grid.sort_defaults = [
|
||||
mod.SortInfo('name', 'asc'),
|
||||
mod.SortInfo('value', 'desc'),
|
||||
]
|
||||
self.assertFalse(hasattr(grid, 'active_sorters'))
|
||||
grid.load_settings()
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'asc'}])
|
||||
|
||||
# load settings from session when nothing is in request
|
||||
self.request.GET = {}
|
||||
self.request.session.invalidate()
|
||||
self.assertNotIn('grid.settings.sorters.length', self.request.session)
|
||||
self.request.session['grid.settings.sorters.length'] = 1
|
||||
self.request.session['grid.settings.sorters.1.key'] = 'name'
|
||||
self.request.session['grid.settings.sorters.1.dir'] = 'desc'
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
sortable=True, sort_on_backend=True,
|
||||
paginated=True, paginate_on_backend=True)
|
||||
self.assertFalse(hasattr(grid, 'active_sorters'))
|
||||
grid.load_settings()
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'desc'}])
|
||||
|
||||
def test_sort_data(self):
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
{'name': 'foo1', 'value': 'ONE'},
|
||||
{'name': 'foo2', 'value': 'two'},
|
||||
{'name': 'foo3', 'value': 'three'},
|
||||
{'name': 'foo4', 'value': 'four'},
|
||||
{'name': 'foo5', 'value': 'five'},
|
||||
{'name': 'foo6', 'value': 'six'},
|
||||
{'name': 'foo7', 'value': 'seven'},
|
||||
{'name': 'foo8', 'value': 'eight'},
|
||||
{'name': 'foo9', 'value': 'nine'},
|
||||
]
|
||||
for setting in sample_data:
|
||||
self.app.save_setting(self.session, setting['name'], setting['value'])
|
||||
self.session.commit()
|
||||
sample_query = self.session.query(model.Setting)
|
||||
|
||||
grid = self.make_grid(model_class=model.Setting,
|
||||
sortable=True, sort_on_backend=True,
|
||||
sort_defaults=('name', 'desc'))
|
||||
grid.load_settings()
|
||||
|
||||
# can sort a simple list of data
|
||||
sorted_data = grid.sort_data(sample_data)
|
||||
self.assertIsInstance(sorted_data, list)
|
||||
self.assertEqual(len(sorted_data), 9)
|
||||
self.assertEqual(sorted_data[0]['name'], 'foo9')
|
||||
self.assertEqual(sorted_data[-1]['name'], 'foo1')
|
||||
|
||||
# can also sort a data query
|
||||
sorted_query = grid.sort_data(sample_query)
|
||||
self.assertIsInstance(sorted_query, orm.Query)
|
||||
sorted_data = sorted_query.all()
|
||||
self.assertEqual(len(sorted_data), 9)
|
||||
self.assertEqual(sorted_data[0]['name'], 'foo9')
|
||||
self.assertEqual(sorted_data[-1]['name'], 'foo1')
|
||||
|
||||
# cannot sort data if sorter missing in overrides
|
||||
sorted_data = grid.sort_data(sample_data, sorters=[])
|
||||
# nb. sorted data is in same order as original sample (not sorted)
|
||||
self.assertEqual(sorted_data[0]['name'], 'foo1')
|
||||
self.assertEqual(sorted_data[-1]['name'], 'foo9')
|
||||
|
||||
# error if mult-column sort attempted
|
||||
self.assertRaises(NotImplementedError, grid.sort_data, sample_data, sorters=[
|
||||
{'key': 'name', 'dir': 'desc'},
|
||||
{'key': 'value', 'dir': 'asc'},
|
||||
])
|
||||
|
||||
# cannot sort data if sortfunc is missing for column
|
||||
grid.remove_sorter('name')
|
||||
sorted_data = grid.sort_data(sample_data)
|
||||
# nb. sorted data is in same order as original sample (not sorted)
|
||||
self.assertEqual(sorted_data[0]['name'], 'foo1')
|
||||
self.assertEqual(sorted_data[-1]['name'], 'foo9')
|
||||
|
||||
# cannot sort data if sortfunc is missing for column
|
||||
grid.remove_sorter('name')
|
||||
# nb. attempting multi-column sort, but only one sorter exists
|
||||
self.assertEqual(list(grid.sorters), ['value'])
|
||||
grid.active_sorters = [{'key': 'name', 'dir': 'asc'},
|
||||
{'key': 'value', 'dir': 'asc'}]
|
||||
with patch.object(sample_query, 'order_by') as order_by:
|
||||
order_by.return_value = 42
|
||||
sorted_query = grid.sort_data(sample_query)
|
||||
order_by.assert_called_once()
|
||||
self.assertEqual(len(order_by.call_args.args), 1)
|
||||
self.assertEqual(sorted_query, 42)
|
||||
|
||||
def test_render_vue_tag(self):
|
||||
model = self.app.model
|
||||
|
||||
|
@ -249,11 +488,13 @@ class TestGrid(WebTestCase):
|
|||
model = self.app.model
|
||||
|
||||
# sanity check
|
||||
grid = self.make_grid('settings', model_class=model.Setting)
|
||||
grid = self.make_grid('settings', model_class=model.Setting, sortable=True)
|
||||
columns = grid.get_vue_columns()
|
||||
self.assertEqual(len(columns), 2)
|
||||
self.assertEqual(columns[0]['field'], 'name')
|
||||
self.assertTrue(columns[0]['sortable'])
|
||||
self.assertEqual(columns[1]['field'], 'value')
|
||||
self.assertTrue(columns[1]['sortable'])
|
||||
|
||||
def test_get_vue_data(self):
|
||||
model = self.app.model
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue