diff --git a/CHANGELOG.md b/CHANGELOG.md index ef4a5e4..cc241e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to wuttaweb will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.20.2 (2025-01-14) + +### Fix + +- improve support for composite `model_key` in MasterView +- let content header text be a bit longer +- add optional `target` attr for GridAction +- add `render_date()` method for grids + ## v0.20.1 (2025-01-13) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 8ec3f7c..a7a756e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttaWeb" -version = "0.20.1" +version = "0.20.2" description = "Web App for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py index fb797a4..e65a22e 100644 --- a/src/wuttaweb/grids/base.py +++ b/src/wuttaweb/grids/base.py @@ -2280,6 +2280,10 @@ class GridAction: See also :meth:`get_url()`. + .. attribute:: target + + Optional ``target`` attribute for the ```` tag. + .. attribute:: icon Name of icon to be shown for the action link. @@ -2297,6 +2301,7 @@ class GridAction: key, label=None, url=None, + target=None, icon=None, link_class=None, ): @@ -2305,6 +2310,7 @@ class GridAction: self.app = self.config.get_app() self.key = key self.url = url + self.target = target self.label = label or self.app.make_title(key) self.icon = icon or key self.link_class = link_class or '' diff --git a/src/wuttaweb/templates/base.mako b/src/wuttaweb/templates/base.mako index 429e9be..486b496 100644 --- a/src/wuttaweb/templates/base.mako +++ b/src/wuttaweb/templates/base.mako @@ -155,7 +155,7 @@ } #content-title h1 { - max-width: 80%; + max-width: 85%; overflow: hidden; padding-left: 0.5rem; text-overflow: ellipsis; diff --git a/src/wuttaweb/templates/grids/vue_template.mako b/src/wuttaweb/templates/grids/vue_template.mako index 746a939..ac0a2a9 100644 --- a/src/wuttaweb/templates/grids/vue_template.mako +++ b/src/wuttaweb/templates/grids/vue_template.mako @@ -180,6 +180,9 @@ % for action in grid.actions: ${action.render_icon_and_label()} diff --git a/src/wuttaweb/views/master.py b/src/wuttaweb/views/master.py index cb32ef9..61d4d14 100644 --- a/src/wuttaweb/views/master.py +++ b/src/wuttaweb/views/master.py @@ -2176,6 +2176,28 @@ class MasterView(View): """ return str(instance) or "(no title)" + def get_action_route_kwargs(self, obj): + """ + Get a dict of route kwargs for the given object. + + This is called from :meth:`get_action_url()` and must return + kwargs suitable for use with ``request.route_url()``. + + In practice this should return a dict which has keys for each + field from :meth:`get_model_key()` and values which come from + the object. + + :param obj: Model instance object. + + :returns: The dict of route kwargs for the object. + """ + try: + return dict([(key, obj[key]) + for key in self.get_model_key()]) + except TypeError: + return dict([(key, getattr(obj, key)) + for key in self.get_model_key()]) + def get_action_url(self, action, obj, **kwargs): """ Generate an "action" URL for the given model instance. @@ -2183,22 +2205,21 @@ class MasterView(View): This is a shortcut which generates a route name based on :meth:`get_route_prefix()` and the ``action`` param. - It returns the URL based on generated route name and object's - model key values. + It calls :meth:`get_action_route_kwargs()` and then passes + those along with route name to ``request.route_url()``, and + returns the result. :param action: String name for the action, which corresponds to part of some named route, e.g. ``'view'`` or ``'edit'``. :param obj: Model instance object. + + :param \**kwargs: Additional kwargs to be passed to + ``request.route_url()``, if needed. """ - route_prefix = self.get_route_prefix() - 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 = self.get_action_route_kwargs(obj) kw.update(kwargs) + route_prefix = self.get_route_prefix() return self.request.route_url(f'{route_prefix}.{action}', **kw) def get_action_url_view(self, obj, i): @@ -2729,7 +2750,7 @@ class MasterView(View): inspector = sa.inspect(model_class) keys = [col.name for col in inspector.primary_key] return tuple([prop.key for prop in inspector.column_attrs - if [col.name for col in prop.columns] == keys]) + if all([col.name in keys for col in prop.columns])]) raise AttributeError(f"you must define model_key for view class: {cls}") diff --git a/tests/views/test_master.py b/tests/views/test_master.py index d77e2b8..c259034 100644 --- a/tests/views/test_master.py +++ b/tests/views/test_master.py @@ -750,6 +750,29 @@ class TestMasterView(WebTestCase): self.request.matchdict = {'name': 'blarg'} self.assertRaises(HTTPNotFound, view.get_instance, session=self.session) + def test_get_action_route_kwargs(self): + model = self.app.model + with patch.object(mod.MasterView, 'model_class', new=model.Setting, create=True): + view = self.make_view() + + # dict object + setting = {'name': 'foo', 'value': 'bar'} + kw = view.get_action_route_kwargs(setting) + self.assertEqual(kw, {'name': 'foo'}) + + # mapped object + setting = model.Setting(name='foo', value='bar') + kw = view.get_action_route_kwargs(setting) + self.assertEqual(kw, {'name': 'foo'}) + + # non-standard object + class MySetting: + def __init__(self, **kw): + self.__dict__.update(kw) + setting = MySetting(name='foo', value='bar') + kw = view.get_action_route_kwargs(setting) + self.assertEqual(kw, {'name': 'foo'}) + def test_get_action_url_for_dict(self): model = self.app.model setting = {'name': 'foo', 'value': 'bar'}