feat: add initial filtering logic to grid class
still missing the actual filters, subclass must provide those for now
This commit is contained in:
parent
a042d511fb
commit
9751bf4c2e
2 changed files with 633 additions and 20 deletions
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from sqlalchemy import orm
|
||||
from paginate import Page
|
||||
|
@ -86,6 +86,32 @@ class TestGrid(WebTestCase):
|
|||
sort_multiple=True)
|
||||
self.assertFalse(grid.sort_multiple)
|
||||
|
||||
def test_constructor_filtering(self):
|
||||
model = self.app.model
|
||||
|
||||
# defaults, not filterable
|
||||
grid = self.make_grid()
|
||||
self.assertFalse(grid.filterable)
|
||||
self.assertEqual(grid.filters, {})
|
||||
|
||||
# defaults, filterable
|
||||
grid = self.make_grid(filterable=True)
|
||||
self.assertTrue(grid.filterable)
|
||||
self.assertEqual(grid.filters, {})
|
||||
|
||||
# filters may be pre-populated
|
||||
with patch.object(mod.Grid, 'make_filter', return_value=42):
|
||||
grid = self.make_grid(model_class=model.Setting, filterable=True)
|
||||
self.assertEqual(len(grid.filters), 2)
|
||||
self.assertIn('name', grid.filters)
|
||||
self.assertIn('value', grid.filters)
|
||||
|
||||
# can specify filters
|
||||
grid = self.make_grid(model_class=model.Setting, filterable=True,
|
||||
filters={'name': 42})
|
||||
self.assertTrue(grid.filterable)
|
||||
self.assertEqual(grid.filters, {'name': 42})
|
||||
|
||||
def test_vue_tagname(self):
|
||||
grid = self.make_grid()
|
||||
self.assertEqual(grid.vue_tagname, 'wutta-grid')
|
||||
|
@ -125,17 +151,28 @@ class TestGrid(WebTestCase):
|
|||
self.assertEqual(grid.columns, ['one', 'four'])
|
||||
|
||||
def test_set_label(self):
|
||||
grid = self.make_grid(columns=['foo', 'bar'])
|
||||
model = self.app.model
|
||||
with patch.object(mod.Grid, 'make_filter'):
|
||||
# nb. filters are MagicMock instances
|
||||
grid = self.make_grid(model_class=model.Setting, filterable=True)
|
||||
self.assertEqual(grid.labels, {})
|
||||
|
||||
# basic
|
||||
grid.set_label('foo', "Foo Fighters")
|
||||
self.assertEqual(grid.labels['foo'], "Foo Fighters")
|
||||
grid.set_label('name', "NAME COL")
|
||||
self.assertEqual(grid.labels['name'], "NAME COL")
|
||||
|
||||
# can replace label
|
||||
grid.set_label('foo', "Different")
|
||||
self.assertEqual(grid.labels['foo'], "Different")
|
||||
self.assertEqual(grid.get_label('foo'), "Different")
|
||||
grid.set_label('name', "Different")
|
||||
self.assertEqual(grid.labels['name'], "Different")
|
||||
self.assertEqual(grid.get_label('name'), "Different")
|
||||
|
||||
# can update only column, not filter
|
||||
self.assertEqual(grid.labels, {'name': "Different"})
|
||||
self.assertIn('name', grid.filters)
|
||||
self.assertEqual(grid.filters['name'].label, "Different")
|
||||
grid.set_label('name', "COLUMN ONLY", column_only=True)
|
||||
self.assertEqual(grid.get_label('name'), "COLUMN ONLY")
|
||||
self.assertEqual(grid.filters['name'].label, "Different")
|
||||
|
||||
def test_get_label(self):
|
||||
grid = self.make_grid(columns=['foo', 'bar'])
|
||||
|
@ -342,20 +379,72 @@ class TestGrid(WebTestCase):
|
|||
grid.load_settings()
|
||||
self.assertEqual(grid.active_sorters, [{'key': 'name', 'dir': 'desc'}])
|
||||
|
||||
# filter settings are loaded, applied, saved
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
filterable=True)
|
||||
self.assertEqual(len(grid.filters), 2)
|
||||
self.assertFalse(hasattr(grid.filters['name'], 'active'))
|
||||
self.assertFalse(hasattr(grid.filters['value'], 'active'))
|
||||
self.assertNotIn('grid.settings.filter.name.active', self.request.session)
|
||||
self.assertNotIn('grid.settings.filter.value.active', self.request.session)
|
||||
self.request.GET = {'name': 'john', 'name.verb': 'contains'}
|
||||
grid.load_settings()
|
||||
self.assertTrue(grid.filters['name'].active)
|
||||
self.assertEqual(grid.filters['name'].verb, 'contains')
|
||||
self.assertEqual(grid.filters['name'].value, 'john')
|
||||
self.assertTrue(self.request.session['grid.settings.filter.name.active'])
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.verb'], 'contains')
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.value'], 'john')
|
||||
|
||||
# filter + sort settings are loaded, applied, saved
|
||||
self.request.session.invalidate()
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
sortable=True, filterable=True)
|
||||
self.assertEqual(len(grid.filters), 2)
|
||||
self.assertFalse(hasattr(grid.filters['name'], 'active'))
|
||||
self.assertFalse(hasattr(grid.filters['value'], 'active'))
|
||||
self.assertNotIn('grid.settings.filter.name.active', self.request.session)
|
||||
self.assertNotIn('grid.settings.filter.value.active', self.request.session)
|
||||
self.assertNotIn('grid.settings.sorters.length', self.request.session)
|
||||
self.request.GET = {'name': 'john', 'name.verb': 'contains',
|
||||
'sort1key': 'name', 'sort1dir': 'asc'}
|
||||
grid.load_settings()
|
||||
self.assertTrue(grid.filters['name'].active)
|
||||
self.assertEqual(grid.filters['name'].verb, 'contains')
|
||||
self.assertEqual(grid.filters['name'].value, 'john')
|
||||
self.assertTrue(self.request.session['grid.settings.filter.name.active'])
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.verb'], 'contains')
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.value'], 'john')
|
||||
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'], 'asc')
|
||||
|
||||
def test_request_has_settings(self):
|
||||
grid = self.make_grid(key='foo')
|
||||
model = self.app.model
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting)
|
||||
|
||||
# paging
|
||||
self.assertFalse(grid.request_has_settings('page'))
|
||||
with patch.object(self.request, 'GET', new={'pagesize': '20'}):
|
||||
self.assertTrue(grid.request_has_settings('page'))
|
||||
with patch.object(self.request, 'GET', new={'page': '1'}):
|
||||
self.assertTrue(grid.request_has_settings('page'))
|
||||
with patch.object(grid, 'paginated', new=True):
|
||||
with patch.object(self.request, 'GET', new={'pagesize': '20'}):
|
||||
self.assertTrue(grid.request_has_settings('page'))
|
||||
with patch.object(self.request, 'GET', new={'page': '1'}):
|
||||
self.assertTrue(grid.request_has_settings('page'))
|
||||
|
||||
# sorting
|
||||
self.assertFalse(grid.request_has_settings('sort'))
|
||||
with patch.object(self.request, 'GET', new={'sort1key': 'name'}):
|
||||
self.assertTrue(grid.request_has_settings('sort'))
|
||||
with patch.object(grid, 'sortable', new=True):
|
||||
with patch.object(self.request, 'GET', new={'sort1key': 'name'}):
|
||||
self.assertTrue(grid.request_has_settings('sort'))
|
||||
|
||||
# filtering
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting, filterable=True)
|
||||
self.assertFalse(grid.request_has_settings('filter'))
|
||||
with patch.object(grid, 'filterable', new=True):
|
||||
with patch.object(self.request, 'GET', new={'name': 'john', 'name.verb': 'contains'}):
|
||||
self.assertTrue(grid.request_has_settings('filter'))
|
||||
with patch.object(self.request, 'GET', new={'filter': '1'}):
|
||||
self.assertTrue(grid.request_has_settings('filter'))
|
||||
|
||||
def test_get_setting(self):
|
||||
grid = self.make_grid(key='foo')
|
||||
|
@ -400,6 +489,40 @@ class TestGrid(WebTestCase):
|
|||
value = grid.get_setting(settings, 'pagesize', src='session', normalize=int)
|
||||
self.assertEqual(value, 35)
|
||||
|
||||
def test_update_filter_settings(self):
|
||||
model = self.app.model
|
||||
|
||||
# nothing happens if not filterable
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting)
|
||||
settings = {}
|
||||
self.request.session['grid.settings.filter.name.active'] = True
|
||||
self.request.session['grid.settings.filter.name.verb'] = 'contains'
|
||||
self.request.session['grid.settings.filter.name.value'] = 'john'
|
||||
grid.update_filter_settings(settings, src='session')
|
||||
self.assertEqual(settings, {})
|
||||
|
||||
# nb. now use a filterable grid
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
filterable=True)
|
||||
|
||||
# settings are updated from session
|
||||
settings = {}
|
||||
self.request.session['grid.settings.filter.name.active'] = True
|
||||
self.request.session['grid.settings.filter.name.verb'] = 'contains'
|
||||
self.request.session['grid.settings.filter.name.value'] = 'john'
|
||||
grid.update_filter_settings(settings, src='session')
|
||||
self.assertTrue(settings['filter.name.active'])
|
||||
self.assertEqual(settings['filter.name.verb'], 'contains')
|
||||
self.assertEqual(settings['filter.name.value'], 'john')
|
||||
|
||||
# settings are updated from request
|
||||
self.request.GET = {'value': 'sally', 'value.verb': 'contains'}
|
||||
grid.update_filter_settings(settings, src='request')
|
||||
self.assertFalse(settings['filter.name.active'])
|
||||
self.assertTrue(settings['filter.value.active'])
|
||||
self.assertEqual(settings['filter.value.verb'], 'contains')
|
||||
self.assertEqual(settings['filter.value.value'], 'sally')
|
||||
|
||||
def test_update_sort_settings(self):
|
||||
model = self.app.model
|
||||
|
||||
|
@ -510,6 +633,25 @@ class TestGrid(WebTestCase):
|
|||
self.assertNotIn('grid.settings.sorters.2.key', self.request.session)
|
||||
self.assertNotIn('grid.settings.sorters.2.dir', self.request.session)
|
||||
|
||||
# nb. now switch to filterable-only grid
|
||||
grid = self.make_grid(key='settings', model_class=model.Setting,
|
||||
filterable=True)
|
||||
self.assertIn('name', grid.filters)
|
||||
self.assertEqual(grid.filters['name'].key, 'name')
|
||||
|
||||
# no error if empty settings; does not save values
|
||||
grid.persist_settings({}, dest='session')
|
||||
self.assertNotIn('grid.settings.filters.name', self.request.session)
|
||||
|
||||
# provided values are saved
|
||||
grid.persist_settings({'filter.name.active': True,
|
||||
'filter.name.verb': 'contains',
|
||||
'filter.name.value': 'john'},
|
||||
dest='session')
|
||||
self.assertTrue(self.request.session['grid.settings.filter.name.active'])
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.verb'], 'contains')
|
||||
self.assertEqual(self.request.session['grid.settings.filter.name.value'], 'john')
|
||||
|
||||
##############################
|
||||
# sorting methods
|
||||
##############################
|
||||
|
@ -629,6 +771,23 @@ class TestGrid(WebTestCase):
|
|||
sorted_data = sorter(sample_data, 'asc')
|
||||
self.assertEqual(dict(sorted_data[0]), {'name': 'foo1', 'value': 'ONE'})
|
||||
|
||||
def test_set_joiner(self):
|
||||
|
||||
# basic
|
||||
grid = self.make_grid(columns=['foo', 'bar'], sortable=True, sort_on_backend=True)
|
||||
self.assertEqual(grid.joiners, {})
|
||||
grid.set_joiner('foo', 42)
|
||||
self.assertEqual(grid.joiners, {'foo': 42})
|
||||
|
||||
def test_remove_joiner(self):
|
||||
|
||||
# basic
|
||||
grid = self.make_grid(columns=['foo', 'bar'], sortable=True, sort_on_backend=True,
|
||||
joiners={'foo': 42})
|
||||
self.assertEqual(grid.joiners, {'foo': 42})
|
||||
grid.remove_joiner('foo')
|
||||
self.assertEqual(grid.joiners, {})
|
||||
|
||||
def test_set_sorter(self):
|
||||
model = self.app.model
|
||||
|
||||
|
@ -726,6 +885,89 @@ class TestGrid(WebTestCase):
|
|||
grid.sortable = False
|
||||
self.assertFalse(grid.is_sortable('name'))
|
||||
|
||||
def test_make_backend_filters(self):
|
||||
model = self.app.model
|
||||
|
||||
# default is empty
|
||||
grid = self.make_grid()
|
||||
filters = grid.make_backend_filters()
|
||||
self.assertEqual(filters, {})
|
||||
|
||||
# makes filters if model class
|
||||
with patch.object(mod.Grid, 'make_filter'):
|
||||
# nb. filters are MagicMock instances
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
filters = grid.make_backend_filters()
|
||||
self.assertEqual(len(filters), 2)
|
||||
self.assertIn('name', filters)
|
||||
self.assertIn('value', filters)
|
||||
|
||||
# does not replace supplied filters
|
||||
myfilters = {'value': 42}
|
||||
with patch.object(mod.Grid, 'make_filter'):
|
||||
# nb. filters are MagicMock instances
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
filters = grid.make_backend_filters(myfilters)
|
||||
self.assertEqual(len(filters), 2)
|
||||
self.assertIn('name', filters)
|
||||
self.assertIn('value', filters)
|
||||
self.assertEqual(filters['value'], 42)
|
||||
self.assertEqual(myfilters['value'], 42)
|
||||
|
||||
def test_make_filter(self):
|
||||
model = self.app.model
|
||||
|
||||
# basic
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
filtr = grid.make_filter('name')
|
||||
self.assertIsInstance(filtr, mod.GridFilter)
|
||||
|
||||
# property
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
filtr = grid.make_filter(model.Setting.name)
|
||||
self.assertIsInstance(filtr, mod.GridFilter)
|
||||
|
||||
def test_set_filter(self):
|
||||
model = self.app.model
|
||||
|
||||
with patch.object(mod.Grid, 'make_filter', return_value=42):
|
||||
|
||||
# auto from model property
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
self.assertEqual(grid.filters, {})
|
||||
grid.set_filter('name', model.Setting.name)
|
||||
self.assertIn('name', grid.filters)
|
||||
|
||||
# auto from column name
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
self.assertEqual(grid.filters, {})
|
||||
grid.set_filter('name', 'name')
|
||||
self.assertIn('name', grid.filters)
|
||||
|
||||
# auto from key
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
self.assertEqual(grid.filters, {})
|
||||
grid.set_filter('name')
|
||||
self.assertIn('name', grid.filters)
|
||||
|
||||
# explicit is not yet implemented
|
||||
grid = self.make_grid(model_class=model.Setting)
|
||||
self.assertEqual(grid.filters, {})
|
||||
self.assertRaises(NotImplementedError, grid.set_filter, 'name', lambda q: q)
|
||||
|
||||
def test_remove_filter(self):
|
||||
model = self.app.model
|
||||
|
||||
# basics
|
||||
with patch.object(mod.Grid, 'make_filter'):
|
||||
# nb. filters are MagicMock instances
|
||||
grid = self.make_grid(model_class=model.Setting, filterable=True)
|
||||
self.assertEqual(len(grid.filters), 2)
|
||||
self.assertIn('name', grid.filters)
|
||||
self.assertIn('value', grid.filters)
|
||||
grid.remove_filter('value')
|
||||
self.assertNotIn('value', grid.filters)
|
||||
|
||||
##############################
|
||||
# data methods
|
||||
##############################
|
||||
|
@ -751,14 +993,27 @@ class TestGrid(WebTestCase):
|
|||
# data is sorted and paginated
|
||||
grid = self.make_grid(model_class=model.Setting,
|
||||
data=sample_query,
|
||||
filterable=True,
|
||||
sortable=True, sort_on_backend=True,
|
||||
sort_defaults=('name', 'desc'),
|
||||
paginated=True, paginate_on_backend=True,
|
||||
pagesize=4, page=2)
|
||||
grid.load_settings()
|
||||
visible = grid.get_visible_data()
|
||||
# nb. for now the filtering is mocked
|
||||
with patch.object(grid, 'filter_data') as filter_data:
|
||||
filter_data.side_effect = lambda q: q
|
||||
visible = grid.get_visible_data()
|
||||
filter_data.assert_called_once_with(sample_query)
|
||||
self.assertEqual([s.name for s in visible], ['foo5', 'foo4', 'foo3', 'foo2'])
|
||||
|
||||
def test_filter_data(self):
|
||||
model = self.app.model
|
||||
|
||||
query = self.session.query(model.Setting)
|
||||
grid = self.make_grid(model_class=model.Setting, filterable=True)
|
||||
grid.load_settings()
|
||||
self.assertRaises(NotImplementedError, grid.filter_data, query)
|
||||
|
||||
def test_sort_data(self):
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
|
@ -827,6 +1082,21 @@ class TestGrid(WebTestCase):
|
|||
self.assertEqual(sorted_data[0]['name'], 'foo1')
|
||||
self.assertEqual(sorted_data[-1]['name'], 'foo9')
|
||||
|
||||
# now try with a joiner
|
||||
query = self.session.query(model.User)
|
||||
grid = self.make_grid(model_class=model.User,
|
||||
data=query,
|
||||
columns=['username', 'full_name'],
|
||||
sortable=True, sort_on_backend=True,
|
||||
sort_defaults='full_name',
|
||||
joiners={
|
||||
'full_name': lambda q: q.join(model.Person),
|
||||
})
|
||||
grid.set_sorter('full_name', model.Person.full_name)
|
||||
grid.load_settings()
|
||||
data = grid.get_visible_data()
|
||||
self.assertIsInstance(data, orm.Query)
|
||||
|
||||
def test_paginate_data(self):
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue