From dcdc0e7daba618f954b562300669b255099826e2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 25 Nov 2024 19:11:41 -0600 Subject: [PATCH] fix: improve support for random objects with grid, master view thus far we expected either dict or "native" ORM object which can essentially behave like a dict when needed. but a "non-native" object may not behave like a dict and this hopefully fixes the logic to allow for those anyway.. --- src/wuttaweb/grids/base.py | 11 ++++++++++- src/wuttaweb/views/master.py | 8 ++++++-- tests/grids/test_base.py | 19 +++++++++++++++++++ tests/views/test_master.py | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py index 4ff990e..71b6a69 100644 --- a/src/wuttaweb/grids/base.py +++ b/src/wuttaweb/grids/base.py @@ -1940,6 +1940,15 @@ class Grid: }) return filters + def object_to_dict(self, obj): + """ """ + try: + dct = dict(obj) + except TypeError: + dct = dict(obj.__dict__) + dct.pop('_sa_instance_state', None) + return dct + def get_vue_context(self): """ Returns a dict of context for the grid, for use with the Vue @@ -1976,7 +1985,7 @@ class Grid: original_record = record # convert record to new dict - record = dict(record) + record = self.object_to_dict(record) # make all values safe for json record = make_json_safe(record, warn=False) diff --git a/src/wuttaweb/views/master.py b/src/wuttaweb/views/master.py index 6f3967e..6e70c4d 100644 --- a/src/wuttaweb/views/master.py +++ b/src/wuttaweb/views/master.py @@ -2018,8 +2018,12 @@ class MasterView(View): :param obj: Model instance object. """ route_prefix = self.get_route_prefix() - kw = dict([(key, obj[key]) - for key in self.get_model_key()]) + try: + kw = dict([(key, obj[key]) + for key in self.get_model_key()]) + except TypeError: + kw = dict([(key, getattr(obj, key)) + for key in self.get_model_key()]) kw.update(kwargs) return self.request.route_url(f'{route_prefix}.{action}', **kw) diff --git a/tests/grids/test_base.py b/tests/grids/test_base.py index f532ddf..fb939db 100644 --- a/tests/grids/test_base.py +++ b/tests/grids/test_base.py @@ -1373,6 +1373,25 @@ class TestGrid(WebTestCase): filters = grid.get_vue_filters() self.assertEqual(len(filters), 2) + def test_object_to_dict(self): + grid = self.make_grid() + setting = {'name': 'foo', 'value': 'bar'} + + # new dict but with same values + dct = grid.object_to_dict(setting) + self.assertIsInstance(dct, dict) + self.assertIsNot(dct, setting) + self.assertEqual(dct, setting) + + # random object, not iterable + class MockSetting: + def __init__(self, **kw): + self.__dict__.update(kw) + mock = MockSetting(**setting) + dct = grid.object_to_dict(mock) + self.assertIsInstance(dct, dict) + self.assertEqual(dct, setting) + def test_get_vue_context(self): # empty if no columns defined diff --git a/tests/views/test_master.py b/tests/views/test_master.py index 7b75e0a..7e427e9 100644 --- a/tests/views/test_master.py +++ b/tests/views/test_master.py @@ -734,6 +734,41 @@ class TestMasterView(WebTestCase): self.request.matchdict = {'name': 'blarg'} self.assertRaises(HTTPNotFound, view.get_instance, session=self.session) + def test_get_action_url_for_dict(self): + model = self.app.model + setting = {'name': 'foo', 'value': 'bar'} + with patch.multiple(mod.MasterView, create=True, + model_class=model.Setting): + mod.MasterView.defaults(self.pyramid_config) + view = self.make_view() + url = view.get_action_url_view(setting, 0) + self.assertEqual(url, self.request.route_url('settings.view', name='foo')) + + def test_get_action_url_for_orm_object(self): + model = self.app.model + setting = model.Setting(name='foo', value='bar') + self.session.add(setting) + self.session.commit() + with patch.multiple(mod.MasterView, create=True, + model_class=model.Setting): + mod.MasterView.defaults(self.pyramid_config) + view = self.make_view() + url = view.get_action_url_view(setting, 0) + self.assertEqual(url, self.request.route_url('settings.view', name='foo')) + + def test_get_action_url_for_adhoc_object(self): + model = self.app.model + class MockSetting: + def __init__(self, **kw): + self.__dict__.update(kw) + setting = MockSetting(name='foo', value='bar') + with patch.multiple(mod.MasterView, create=True, + model_class=model.Setting): + mod.MasterView.defaults(self.pyramid_config) + view = self.make_view() + url = view.get_action_url_view(setting, 0) + self.assertEqual(url, self.request.route_url('settings.view', name='foo')) + def test_get_action_url_view(self): model = self.app.model setting = model.Setting(name='foo', value='bar')