Compare commits
	
		
			4 commits
		
	
	
		
			a405b19217
			...
			51c456eb38
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 51c456eb38 | |||
| ecb1dce590 | |||
| 72a663a80b | |||
| 59fe324872 | 
					 7 changed files with 74 additions and 12 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"}]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2280,6 +2280,10 @@ class GridAction:
 | 
			
		|||
 | 
			
		||||
       See also :meth:`get_url()`.
 | 
			
		||||
 | 
			
		||||
    .. attribute:: target
 | 
			
		||||
 | 
			
		||||
       Optional ``target`` attribute for the ``<a>`` 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 ''
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,7 +155,7 @@
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    #content-title h1 {
 | 
			
		||||
        max-width: 80%;
 | 
			
		||||
        max-width: 85%;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        padding-left: 0.5rem;
 | 
			
		||||
        text-overflow: ellipsis;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,6 +180,9 @@
 | 
			
		|||
            % for action in grid.actions:
 | 
			
		||||
                <a v-if="props.row._action_url_${action.key}"
 | 
			
		||||
                   :href="props.row._action_url_${action.key}"
 | 
			
		||||
                   % if action.target:
 | 
			
		||||
                       target="${action.target}"
 | 
			
		||||
                   % endif
 | 
			
		||||
                   class="${action.link_class}">
 | 
			
		||||
                  ${action.render_icon_and_label()}
 | 
			
		||||
                </a>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue