Compare commits
	
		
			3 commits
		
	
	
		
			91e10274ea
			...
			dce91a3a96
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						dce91a3a96 | ||
| 
							 | 
						1efaca4e52 | ||
| 
							 | 
						4643aa3e3c | 
					 9 changed files with 58 additions and 37 deletions
				
			
		| 
						 | 
					@ -5,6 +5,12 @@ 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.10.1 (2024-08-19)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Fix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- make `util.get_model_fields()` work with more model classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## v0.10.0 (2024-08-18)
 | 
					## v0.10.0 (2024-08-18)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Feat
 | 
					### Feat
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ build-backend = "hatchling.build"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[project]
 | 
					[project]
 | 
				
			||||||
name = "WuttaWeb"
 | 
					name = "WuttaWeb"
 | 
				
			||||||
version = "0.10.0"
 | 
					version = "0.10.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@edbob.org"}]
 | 
					authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -150,7 +150,7 @@ class WuttaSecurityPolicy:
 | 
				
			||||||
        return auth.has_permission(self.db_session, user, permission)
 | 
					        return auth.has_permission(self.db_session, user, permission)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def add_permission_group(pyramid_config, key, label=None, overwrite=True):
 | 
					def add_permission_group(pyramid_config, groupkey, label=None, overwrite=True):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Pyramid directive to add a "permission group" to the app's
 | 
					    Pyramid directive to add a "permission group" to the app's
 | 
				
			||||||
    awareness.
 | 
					    awareness.
 | 
				
			||||||
| 
						 | 
					@ -169,12 +169,12 @@ def add_permission_group(pyramid_config, key, label=None, overwrite=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       pyramid_config.add_permission_group('widgets', label="Widgets")
 | 
					       pyramid_config.add_permission_group('widgets', label="Widgets")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param key: Unique key for the permission group.  In the context
 | 
					    :param groupkey: Unique key for the permission group.  In the
 | 
				
			||||||
       of a master view, this will be the same as
 | 
					       context of a master view, this will be the same as
 | 
				
			||||||
       :attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
 | 
					       :attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param label: Optional label for the permission group.  If not
 | 
					    :param label: Optional label for the permission group.  If not
 | 
				
			||||||
       specified, it is derived from ``key``.
 | 
					       specified, it is derived from ``groupkey``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param overwrite: If the permission group was already established,
 | 
					    :param overwrite: If the permission group was already established,
 | 
				
			||||||
       this flag controls whether the group's label should be
 | 
					       this flag controls whether the group's label should be
 | 
				
			||||||
| 
						 | 
					@ -186,9 +186,9 @@ def add_permission_group(pyramid_config, key, label=None, overwrite=True):
 | 
				
			||||||
    app = config.get_app()
 | 
					    app = config.get_app()
 | 
				
			||||||
    def action():
 | 
					    def action():
 | 
				
			||||||
        perms = pyramid_config.get_settings().get('wutta_permissions', {})
 | 
					        perms = pyramid_config.get_settings().get('wutta_permissions', {})
 | 
				
			||||||
        if overwrite or key not in perms:
 | 
					        if overwrite or groupkey not in perms:
 | 
				
			||||||
            group = perms.setdefault(key, {'key': key})
 | 
					            group = perms.setdefault(groupkey, {'key': groupkey})
 | 
				
			||||||
            group['label'] = label or app.make_title(key)
 | 
					            group['label'] = label or app.make_title(groupkey)
 | 
				
			||||||
        pyramid_config.add_settings({'wutta_permissions': perms})
 | 
					        pyramid_config.add_settings({'wutta_permissions': perms})
 | 
				
			||||||
    pyramid_config.action(None, action)
 | 
					    pyramid_config.action(None, action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -215,8 +215,8 @@ def add_permission(pyramid_config, groupkey, key, label=None):
 | 
				
			||||||
       pyramid_config.add_permission('widgets', 'widgets.polish',
 | 
					       pyramid_config.add_permission('widgets', 'widgets.polish',
 | 
				
			||||||
                                     label="Polish all the widgets")
 | 
					                                     label="Polish all the widgets")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param key: Unique key for the permission group.  In the context
 | 
					    :param groupkey: Unique key for the permission group.  In the
 | 
				
			||||||
       of a master view, this will be the same as
 | 
					       context of a master view, this will be the same as
 | 
				
			||||||
       :attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
 | 
					       :attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param key: Unique key for the permission.  This should be the
 | 
					    :param key: Unique key for the permission.  This should be the
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,6 +182,8 @@ class Form:
 | 
				
			||||||
       String name for Vue component tag.  By default this is
 | 
					       String name for Vue component tag.  By default this is
 | 
				
			||||||
       ``'wutta-form'``.  See also :meth:`render_vue_tag()`.
 | 
					       ``'wutta-form'``.  See also :meth:`render_vue_tag()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       See also :attr:`vue_component`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: align_buttons_right
 | 
					    .. attribute:: align_buttons_right
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       Flag indicating whether the buttons (submit, cancel etc.)
 | 
					       Flag indicating whether the buttons (submit, cancel etc.)
 | 
				
			||||||
| 
						 | 
					@ -784,6 +786,13 @@ class Form:
 | 
				
			||||||
             </form>
 | 
					             </form>
 | 
				
			||||||
           </script>
 | 
					           </script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           <script>
 | 
				
			||||||
 | 
					               WuttaFormData = {}
 | 
				
			||||||
 | 
					               WuttaForm = {
 | 
				
			||||||
 | 
					                   template: 'wutta-form-template',
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					           </script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .. todo::
 | 
					        .. todo::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           Why can't Sphinx render the above code block as 'html' ?
 | 
					           Why can't Sphinx render the above code block as 'html' ?
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,9 +86,9 @@ class Grid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: columns
 | 
					    .. attribute:: columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       :class:`~wuttaweb.forms.base.FieldList` instance containing
 | 
					       :class:`~wuttaweb.util.FieldList` instance containing string
 | 
				
			||||||
       string column names for the grid.  Columns will appear in the
 | 
					       column names for the grid.  Columns will appear in the same
 | 
				
			||||||
       same order as they are in this list.
 | 
					       order as they are in this list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       See also :meth:`set_columns()` and :meth:`get_columns()`.
 | 
					       See also :meth:`set_columns()` and :meth:`get_columns()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,9 +239,9 @@ class Grid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: paginated
 | 
					    .. attribute:: paginated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       Boolean indicating whether the grid data should be paginated
 | 
					       Boolean indicating whether the grid data should be paginated,
 | 
				
			||||||
       vs. all data shown at once.  Default is ``False`` which means
 | 
					       i.e. split up into pages.  Default is ``False`` which means all
 | 
				
			||||||
       the full set of grid data is sent for each request.
 | 
					       data is shown at once.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       See also :attr:`pagesize` and :attr:`page`, and
 | 
					       See also :attr:`pagesize` and :attr:`page`, and
 | 
				
			||||||
       :attr:`paginate_on_backend`.
 | 
					       :attr:`paginate_on_backend`.
 | 
				
			||||||
| 
						 | 
					@ -392,7 +392,7 @@ class Grid:
 | 
				
			||||||
        Explicitly set the list of grid columns.
 | 
					        Explicitly set the list of grid columns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This will overwrite :attr:`columns` with a new
 | 
					        This will overwrite :attr:`columns` with a new
 | 
				
			||||||
        :class:`~wuttaweb.forms.base.FieldList` instance.
 | 
					        :class:`~wuttaweb.util.FieldList` instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param columns: List of string column names.
 | 
					        :param columns: List of string column names.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -440,9 +440,8 @@ class Grid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param label: New label for the column header.
 | 
					        :param label: New label for the column header.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        See also :meth:`get_label()`.
 | 
					        See also :meth:`get_label()`.  Label overrides are tracked via
 | 
				
			||||||
 | 
					        :attr:`labels`.
 | 
				
			||||||
        Label overrides are tracked via :attr:`labels`.
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.labels[key] = label
 | 
					        self.labels[key] = label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -490,7 +489,7 @@ class Grid:
 | 
				
			||||||
           def render_foo(record, key, value):
 | 
					           def render_foo(record, key, value):
 | 
				
			||||||
              return HTML.literal("<p>this is the final cell value</p>")
 | 
					              return HTML.literal("<p>this is the final cell value</p>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           grid = Grid(columns=['foo', 'bar'])
 | 
					           grid = Grid(request, columns=['foo', 'bar'])
 | 
				
			||||||
           grid.set_renderer('foo', render_foo)
 | 
					           grid.set_renderer('foo', render_foo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Renderer overrides are tracked via :attr:`renderers`.
 | 
					        Renderer overrides are tracked via :attr:`renderers`.
 | 
				
			||||||
| 
						 | 
					@ -509,8 +508,8 @@ class Grid:
 | 
				
			||||||
        URL for this will be the same as for the "View"
 | 
					        URL for this will be the same as for the "View"
 | 
				
			||||||
        :class:`GridAction`
 | 
					        :class:`GridAction`
 | 
				
			||||||
        (aka. :meth:`~wuttaweb.views.master.MasterView.view()`).
 | 
					        (aka. :meth:`~wuttaweb.views.master.MasterView.view()`).
 | 
				
			||||||
        Although of course each cell gets a different link depending
 | 
					        Although of course each cell in the column gets a different
 | 
				
			||||||
        on which data record it points to.
 | 
					        link depending on which data record it points to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        It is typical to enable auto-link for fields relating to ID,
 | 
					        It is typical to enable auto-link for fields relating to ID,
 | 
				
			||||||
        description etc. or some may prefer to auto-link all columns.
 | 
					        description etc. or some may prefer to auto-link all columns.
 | 
				
			||||||
| 
						 | 
					@ -609,8 +608,8 @@ class Grid:
 | 
				
			||||||
        The term "model property" is a bit technical, an example
 | 
					        The term "model property" is a bit technical, an example
 | 
				
			||||||
        should help to clarify::
 | 
					        should help to clarify::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           model = self.app.model
 | 
					           model = app.model
 | 
				
			||||||
           grid = Grid(self.request, model_class=model.Person)
 | 
					           grid = Grid(request, model_class=model.Person)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           # explicit property
 | 
					           # explicit property
 | 
				
			||||||
           sorter = grid.make_sorter(model.Person.full_name)
 | 
					           sorter = grid.make_sorter(model.Person.full_name)
 | 
				
			||||||
| 
						 | 
					@ -633,7 +632,7 @@ class Grid:
 | 
				
			||||||
           ]
 | 
					           ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           # nb. no model_class, just as an example
 | 
					           # nb. no model_class, just as an example
 | 
				
			||||||
           grid = Grid(self.request, columns=['foo', 'bar'], data=data)
 | 
					           grid = Grid(request, columns=['foo', 'bar'], data=data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           def getkey(obj):
 | 
					           def getkey(obj):
 | 
				
			||||||
               if obj.get('foo')
 | 
					               if obj.get('foo')
 | 
				
			||||||
| 
						 | 
					@ -725,8 +724,8 @@ class Grid:
 | 
				
			||||||
        A backend sorter callable must accept ``(data, direction)``
 | 
					        A backend sorter callable must accept ``(data, direction)``
 | 
				
			||||||
        args and return the sorted data/query, for example::
 | 
					        args and return the sorted data/query, for example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           model = self.app.model
 | 
					           model = app.model
 | 
				
			||||||
           grid = Grid(self.request, model_class=model.Person)
 | 
					           grid = Grid(request, model_class=model.Person)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           def sort_full_name(query, direction):
 | 
					           def sort_full_name(query, direction):
 | 
				
			||||||
              sortspec = getattr(model.Person.full_name, direction)
 | 
					              sortspec = getattr(model.Person.full_name, direction)
 | 
				
			||||||
| 
						 | 
					@ -750,6 +749,10 @@ class Grid:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Remove the backend sorter for a column.
 | 
					        Remove the backend sorter for a column.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Note that this removes the sorter *function*, so there is
 | 
				
			||||||
 | 
					        no way to sort by this column unless another sorter is
 | 
				
			||||||
 | 
					        later defined for it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        See also :meth:`set_sorter()`.
 | 
					        See also :meth:`set_sorter()`.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.sorters.pop(key, None)
 | 
					        self.sorters.pop(key, None)
 | 
				
			||||||
| 
						 | 
					@ -1140,6 +1143,7 @@ class Grid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        See also these methods which may be called by this one:
 | 
					        See also these methods which may be called by this one:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        * :meth:`sort_data()`
 | 
				
			||||||
        * :meth:`paginate_data()`
 | 
					        * :meth:`paginate_data()`
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        data = self.data or []
 | 
					        data = self.data or []
 | 
				
			||||||
| 
						 | 
					@ -1283,6 +1287,7 @@ class Grid:
 | 
				
			||||||
           {
 | 
					           {
 | 
				
			||||||
               'field': 'foo',
 | 
					               'field': 'foo',
 | 
				
			||||||
               'label': "Foo",
 | 
					               'label': "Foo",
 | 
				
			||||||
 | 
					               'sortable': True,
 | 
				
			||||||
           }
 | 
					           }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        See also :meth:`get_vue_data()`.
 | 
					        See also :meth:`get_vue_data()`.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -462,12 +462,11 @@ def get_model_fields(config, model_class=None):
 | 
				
			||||||
    if not model_class:
 | 
					    if not model_class:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app = config.get_app()
 | 
					    try:
 | 
				
			||||||
    model = app.model
 | 
					 | 
				
			||||||
    if not issubclass(model_class, model.Base):
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mapper = sa.inspect(model_class)
 | 
					        mapper = sa.inspect(model_class)
 | 
				
			||||||
 | 
					    except sa.exc.NoInspectionAvailable:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
        fields = [prop.key for prop in mapper.iterate_properties]
 | 
					        fields = [prop.key for prop in mapper.iterate_properties]
 | 
				
			||||||
        return fields
 | 
					        return fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -259,7 +259,7 @@ class MasterView(View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: form_fields
 | 
					    .. attribute:: form_fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       List of columns for the model form.
 | 
					       List of fields for the model form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       This is optional; see also :meth:`get_form_fields()`.
 | 
					       This is optional; see also :meth:`get_form_fields()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,7 @@ class PersonView(MasterView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def defaults(cls, config):
 | 
					    def defaults(cls, config):
 | 
				
			||||||
 | 
					        """ """
 | 
				
			||||||
        cls._defaults(config)
 | 
					        cls._defaults(config)
 | 
				
			||||||
        cls._people_defaults(config)
 | 
					        cls._people_defaults(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -246,6 +246,7 @@ class RoleView(MasterView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def defaults(cls, config):
 | 
					    def defaults(cls, config):
 | 
				
			||||||
 | 
					        """ """
 | 
				
			||||||
        cls._defaults(config)
 | 
					        cls._defaults(config)
 | 
				
			||||||
        cls._role_defaults(config)
 | 
					        cls._role_defaults(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue