3
0
Fork 0

feat: improve grid filter API a bit, support string/bool filters

This commit is contained in:
Lance Edgar 2024-08-23 12:10:51 -05:00
parent 4525f91c21
commit f6fb6957e3
7 changed files with 919 additions and 271 deletions

View file

@ -3,6 +3,7 @@
from unittest import TestCase
from unittest.mock import patch, MagicMock
import sqlalchemy as sa
from sqlalchemy import orm
from paginate import Page
from paginate_sqlalchemy import SqlalchemyOrmPage
@ -10,6 +11,7 @@ from pyramid import testing
from wuttjamaican.conf import WuttaConfig
from wuttaweb.grids import base as mod
from wuttaweb.grids.filters import GridFilter, StringAlchemyFilter, default_sqlalchemy_filters
from wuttaweb.util import FieldList
from wuttaweb.forms import Form
from tests.util import WebTestCase
@ -921,20 +923,38 @@ class TestGrid(WebTestCase):
def test_make_filter(self):
model = self.app.model
# basic
# arg is column name
grid = self.make_grid(model_class=model.Setting)
filtr = grid.make_filter('name')
self.assertIsInstance(filtr, mod.GridFilter)
self.assertIsInstance(filtr, StringAlchemyFilter)
# property
grid = self.make_grid(model_class=model.Setting)
filtr = grid.make_filter(model.Setting.name)
self.assertIsInstance(filtr, mod.GridFilter)
# invalid model class
# arg is column name, but model class is invalid
grid = self.make_grid(model_class=42)
self.assertRaises(ValueError, grid.make_filter, 'name')
# arg is model property
grid = self.make_grid(model_class=model.Setting)
filtr = grid.make_filter(model.Setting.name)
self.assertIsInstance(filtr, StringAlchemyFilter)
# model property as kwarg
grid = self.make_grid(model_class=model.Setting)
filtr = grid.make_filter(None, model_property=model.Setting.name)
self.assertIsInstance(filtr, StringAlchemyFilter)
# default factory
grid = self.make_grid(model_class=model.Setting)
with patch.dict(default_sqlalchemy_filters, {None: GridFilter}, clear=True):
filtr = grid.make_filter(model.Setting.name)
self.assertIsInstance(filtr, GridFilter)
self.assertNotIsInstance(filtr, StringAlchemyFilter)
# factory override
grid = self.make_grid(model_class=model.Setting)
filtr = grid.make_filter(model.Setting.name, factory=GridFilter)
self.assertIsInstance(filtr, GridFilter)
self.assertNotIsInstance(filtr, StringAlchemyFilter)
def test_set_filter(self):
model = self.app.model
@ -1049,6 +1069,9 @@ class TestGrid(WebTestCase):
sample_query = self.session.query(model.Setting)
grid = self.make_grid(key='settings', model_class=model.Setting, filterable=True)
self.assertEqual(list(grid.filters), ['name', 'value'])
self.assertIsInstance(grid.filters['name'], StringAlchemyFilter)
self.assertIsInstance(grid.filters['value'], StringAlchemyFilter)
# not filtered by default
grid.load_settings()
@ -1421,86 +1444,3 @@ class TestGridAction(TestCase):
action = self.make_action('blarg', url=lambda o, i: '/yeehaw')
url = action.get_url(obj)
self.assertEqual(url, '/yeehaw')
class TestGridFilter(WebTestCase):
def setUp(self):
self.setup_web()
model = self.app.model
self.sample_data = [
{'name': 'foo1', 'value': 'ONE'},
{'name': 'foo2', 'value': 'two'},
{'name': 'foo3', 'value': 'ggg'},
{'name': 'foo4', 'value': 'ggg'},
{'name': 'foo5', 'value': 'ggg'},
{'name': 'foo6', 'value': 'six'},
{'name': 'foo7', 'value': 'seven'},
{'name': 'foo8', 'value': 'eight'},
{'name': 'foo9', 'value': 'nine'},
]
for setting in self.sample_data:
self.app.save_setting(self.session, setting['name'], setting['value'])
self.session.commit()
self.sample_query = self.session.query(model.Setting)
def make_filter(self, model_property, **kwargs):
return mod.GridFilter(self.request, model_property, **kwargs)
def test_repr(self):
model = self.app.model
filtr = self.make_filter(model.Setting.name)
self.assertEqual(repr(filtr), "GridFilter(key='name', active=False, verb='contains', value=None)")
def test_apply_filter(self):
model = self.app.model
filtr = self.make_filter(model.Setting.value)
# default verb used as fallback
self.assertEqual(filtr.default_verb, 'contains')
filtr.verb = None
with patch.object(filtr, 'filter_contains', side_effect=lambda q, v: q) as filter_contains:
filtered_query = filtr.apply_filter(self.sample_query, value='foo')
filter_contains.assert_called_once_with(self.sample_query, 'foo')
self.assertIsNone(filtr.verb)
# filter verb used as fallback
filtr.verb = 'equal'
with patch.object(filtr, 'filter_equal', create=True, side_effect=lambda q, v: q) as filter_equal:
filtered_query = filtr.apply_filter(self.sample_query, value='foo')
filter_equal.assert_called_once_with(self.sample_query, 'foo')
# filter value used as fallback
filtr.verb = 'contains'
filtr.value = 'blarg'
with patch.object(filtr, 'filter_contains', side_effect=lambda q, v: q) as filter_contains:
filtered_query = filtr.apply_filter(self.sample_query)
filter_contains.assert_called_once_with(self.sample_query, 'blarg')
# error if invalid verb
self.assertRaises(mod.VerbNotSupported, filtr.apply_filter,
self.sample_query, verb='doesnotexist')
def test_filter_contains(self):
model = self.app.model
filtr = self.make_filter(model.Setting.value)
self.assertEqual(self.sample_query.count(), 9)
# not filtered for empty value
filtered_query = filtr.filter_contains(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
filtered_query = filtr.filter_contains(self.sample_query, '')
self.assertIs(filtered_query, self.sample_query)
# filtered by value
filtered_query = filtr.filter_contains(self.sample_query, 'ggg')
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 3)
class TestVerbNotSupported(TestCase):
def test_basic(self):
error = mod.VerbNotSupported('equal')
self.assertEqual(str(error), "unknown filter verb not supported: equal")