From b3f1f8b6d9f08838ba826303be5fc6c2450d4837 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 6 Jan 2025 16:56:31 -0600 Subject: [PATCH] fix: improve built-in grid renderer logic - add `render_batch_id()` - allow kwargs for `render_currency()` - caller may specify built-in renderer w/ string identifier --- src/wuttaweb/grids/base.py | 47 ++++++++++++++++++++++++++++++++++---- tests/grids/test_base.py | 16 +++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py index 2259c3f..3a3d4f5 100644 --- a/src/wuttaweb/grids/base.py +++ b/src/wuttaweb/grids/base.py @@ -389,7 +389,6 @@ class Grid: self.key = key self.data = data self.labels = labels or {} - self.renderers = renderers or {} self.row_class = row_class self.actions = actions or [] self.linked_columns = linked_columns or [] @@ -399,6 +398,10 @@ class Grid: self.app = self.config.get_app() self.set_columns(columns or self.get_columns()) + self.renderers = {} + if renderers: + for key, val in renderers.items(): + self.set_renderer(key, val) self.set_default_renderers() self.set_tools(tools) @@ -593,8 +596,29 @@ class Grid: grid = Grid(request, columns=['foo', 'bar']) grid.set_renderer('foo', render_foo) + For convenience, in lieu of a renderer callable, you may + specify one of the following strings, which will be + interpreted as a built-in renderer callable, as shown below: + + * ``'batch_id'`` -> :meth:`render_batch_id()` + * ``'boolean'`` -> :meth:`render_boolean()` + * ``'currency'`` -> :meth:`render_currency()` + * ``'datetime'`` -> :meth:`render_datetime()` + * ``'quantity'`` -> :meth:`render_quantity()` + Renderer overrides are tracked via :attr:`renderers`. """ + builtins = { + 'batch_id': self.render_batch_id, + 'boolean': self.render_boolean, + 'currency': self.render_currency, + 'datetime': self.render_datetime, + 'quantity': self.render_quantity, + } + + if renderer in builtins: + renderer = builtins[renderer] + if kwargs: renderer = functools.partial(renderer, **kwargs) self.renderers[key] = renderer @@ -1759,6 +1783,20 @@ class Grid: # rendering methods ############################## + def render_batch_id(self, obj, key, value): + """ + Column renderer for batch ID values. + + This is not used automatically but you can use it explicitly:: + + grid.set_renderer('foo', 'batch_id') + """ + if value is None: + return "" + + batch_id = int(value) + return f'{batch_id:08d}' + def render_boolean(self, obj, key, value): """ Column renderer for boolean values. @@ -1774,7 +1812,7 @@ class Grid: """ return self.app.render_boolean(value) - def render_currency(self, obj, key, value): + def render_currency(self, obj, key, value, **kwargs): """ Column renderer for currency values. @@ -1784,9 +1822,10 @@ class Grid: This is not used automatically but you can use it explicitly:: - grid.set_renderer('foo', grid.render_currency) + grid.set_renderer('foo', 'currency') + grid.set_renderer('foo', 'currency', scale=4) """ - return self.app.render_currency(value) + return self.app.render_currency(value, **kwargs) def render_datetime(self, obj, key, value): """ diff --git a/tests/grids/test_base.py b/tests/grids/test_base.py index 4282554..0cccbeb 100644 --- a/tests/grids/test_base.py +++ b/tests/grids/test_base.py @@ -208,6 +208,11 @@ class TestGrid(WebTestCase): self.assertIsNot(grid.renderers['foo'], render2) self.assertEqual(grid.renderers['foo'](None, None, None), 42) + # can use built-in string shortcut + grid.set_renderer('foo', 'quantity') + obj = MagicMock(foo=42.00) + self.assertEqual(grid.renderers['foo'](obj, 'foo', 42.00), '42') + def test_set_default_renderer(self): model = self.app.model @@ -1343,6 +1348,17 @@ class TestGrid(WebTestCase): # rendering methods ############################## + def test_render_batch_id(self): + grid = self.make_grid(columns=['foo', 'bar']) + + # null + obj = MagicMock(foo=None) + self.assertEqual(grid.render_batch_id(obj, 'foo', None), "") + + # int + obj = MagicMock(foo=42) + self.assertEqual(grid.render_batch_id(obj, 'foo', 42), "00000042") + def test_render_boolean(self): grid = self.make_grid(columns=['foo', 'bar'])