Compare commits
	
		
			No commits in common. "51c456eb387bdce75891a21db677a23bddaddfb2" and "a405b192178917f67028a2159d20d9c96ecabad7" have entirely different histories.
		
	
	
		
			51c456eb38
			...
			a405b19217
		
	
		
					 7 changed files with 12 additions and 74 deletions
				
			
		| 
						 | 
					@ -5,15 +5,6 @@ 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/)
 | 
					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).
 | 
					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)
 | 
					## v0.20.1 (2025-01-13)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Fix
 | 
					### Fix
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ build-backend = "hatchling.build"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[project]
 | 
					[project]
 | 
				
			||||||
name = "WuttaWeb"
 | 
					name = "WuttaWeb"
 | 
				
			||||||
version = "0.20.2"
 | 
					version = "0.20.1"
 | 
				
			||||||
description = "Web App for Wutta Framework"
 | 
					description = "Web App for Wutta Framework"
 | 
				
			||||||
readme = "README.md"
 | 
					readme = "README.md"
 | 
				
			||||||
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
 | 
					authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2280,10 +2280,6 @@ class GridAction:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       See also :meth:`get_url()`.
 | 
					       See also :meth:`get_url()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: target
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       Optional ``target`` attribute for the ``<a>`` tag.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. attribute:: icon
 | 
					    .. attribute:: icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       Name of icon to be shown for the action link.
 | 
					       Name of icon to be shown for the action link.
 | 
				
			||||||
| 
						 | 
					@ -2301,7 +2297,6 @@ class GridAction:
 | 
				
			||||||
            key,
 | 
					            key,
 | 
				
			||||||
            label=None,
 | 
					            label=None,
 | 
				
			||||||
            url=None,
 | 
					            url=None,
 | 
				
			||||||
            target=None,
 | 
					 | 
				
			||||||
            icon=None,
 | 
					            icon=None,
 | 
				
			||||||
            link_class=None,
 | 
					            link_class=None,
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
| 
						 | 
					@ -2310,7 +2305,6 @@ class GridAction:
 | 
				
			||||||
        self.app = self.config.get_app()
 | 
					        self.app = self.config.get_app()
 | 
				
			||||||
        self.key = key
 | 
					        self.key = key
 | 
				
			||||||
        self.url = url
 | 
					        self.url = url
 | 
				
			||||||
        self.target = target
 | 
					 | 
				
			||||||
        self.label = label or self.app.make_title(key)
 | 
					        self.label = label or self.app.make_title(key)
 | 
				
			||||||
        self.icon = icon or key
 | 
					        self.icon = icon or key
 | 
				
			||||||
        self.link_class = link_class or ''
 | 
					        self.link_class = link_class or ''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,7 +155,7 @@
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #content-title h1 {
 | 
					    #content-title h1 {
 | 
				
			||||||
        max-width: 85%;
 | 
					        max-width: 80%;
 | 
				
			||||||
        overflow: hidden;
 | 
					        overflow: hidden;
 | 
				
			||||||
        padding-left: 0.5rem;
 | 
					        padding-left: 0.5rem;
 | 
				
			||||||
        text-overflow: ellipsis;
 | 
					        text-overflow: ellipsis;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,9 +180,6 @@
 | 
				
			||||||
            % for action in grid.actions:
 | 
					            % for action in grid.actions:
 | 
				
			||||||
                <a v-if="props.row._action_url_${action.key}"
 | 
					                <a v-if="props.row._action_url_${action.key}"
 | 
				
			||||||
                   :href="props.row._action_url_${action.key}"
 | 
					                   :href="props.row._action_url_${action.key}"
 | 
				
			||||||
                   % if action.target:
 | 
					 | 
				
			||||||
                       target="${action.target}"
 | 
					 | 
				
			||||||
                   % endif
 | 
					 | 
				
			||||||
                   class="${action.link_class}">
 | 
					                   class="${action.link_class}">
 | 
				
			||||||
                  ${action.render_icon_and_label()}
 | 
					                  ${action.render_icon_and_label()}
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2176,28 +2176,6 @@ class MasterView(View):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return str(instance) or "(no title)"
 | 
					        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):
 | 
					    def get_action_url(self, action, obj, **kwargs):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Generate an "action" URL for the given model instance.
 | 
					        Generate an "action" URL for the given model instance.
 | 
				
			||||||
| 
						 | 
					@ -2205,21 +2183,22 @@ class MasterView(View):
 | 
				
			||||||
        This is a shortcut which generates a route name based on
 | 
					        This is a shortcut which generates a route name based on
 | 
				
			||||||
        :meth:`get_route_prefix()` and the ``action`` param.
 | 
					        :meth:`get_route_prefix()` and the ``action`` param.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        It calls :meth:`get_action_route_kwargs()` and then passes
 | 
					        It returns the URL based on generated route name and object's
 | 
				
			||||||
        those along with route name to ``request.route_url()``, and
 | 
					        model key values.
 | 
				
			||||||
        returns the result.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param action: String name for the action, which corresponds
 | 
					        :param action: String name for the action, which corresponds
 | 
				
			||||||
           to part of some named route, e.g. ``'view'`` or ``'edit'``.
 | 
					           to part of some named route, e.g. ``'view'`` or ``'edit'``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param obj: Model instance object.
 | 
					        :param obj: Model instance object.
 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param \**kwargs: Additional kwargs to be passed to
 | 
					 | 
				
			||||||
           ``request.route_url()``, if needed.
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        kw = self.get_action_route_kwargs(obj)
 | 
					 | 
				
			||||||
        kw.update(kwargs)
 | 
					 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					        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.update(kwargs)
 | 
				
			||||||
        return self.request.route_url(f'{route_prefix}.{action}', **kw)
 | 
					        return self.request.route_url(f'{route_prefix}.{action}', **kw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_action_url_view(self, obj, i):
 | 
					    def get_action_url_view(self, obj, i):
 | 
				
			||||||
| 
						 | 
					@ -2750,7 +2729,7 @@ class MasterView(View):
 | 
				
			||||||
            inspector = sa.inspect(model_class)
 | 
					            inspector = sa.inspect(model_class)
 | 
				
			||||||
            keys = [col.name for col in inspector.primary_key]
 | 
					            keys = [col.name for col in inspector.primary_key]
 | 
				
			||||||
            return tuple([prop.key for prop in inspector.column_attrs
 | 
					            return tuple([prop.key for prop in inspector.column_attrs
 | 
				
			||||||
                          if all([col.name in keys for col in prop.columns])])
 | 
					                          if [col.name for col in prop.columns] == keys])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raise AttributeError(f"you must define model_key for view class: {cls}")
 | 
					        raise AttributeError(f"you must define model_key for view class: {cls}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -750,29 +750,6 @@ class TestMasterView(WebTestCase):
 | 
				
			||||||
            self.request.matchdict = {'name': 'blarg'}
 | 
					            self.request.matchdict = {'name': 'blarg'}
 | 
				
			||||||
            self.assertRaises(HTTPNotFound, view.get_instance, session=self.session)
 | 
					            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):
 | 
					    def test_get_action_url_for_dict(self):
 | 
				
			||||||
        model = self.app.model
 | 
					        model = self.app.model
 | 
				
			||||||
        setting = {'name': 'foo', 'value': 'bar'}
 | 
					        setting = {'name': 'foo', 'value': 'bar'}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue