diff --git a/CHANGELOG.md b/CHANGELOG.md index 899001e..9849d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +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/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## v0.28.2 (2026-02-25) - -### Fix - -- only show string filter input if data type matches -- move `coerce_value()` method to base `GridFilter` class -- preserve original data type when un-setting filter choices -- raise better error if field widget serialization fails -- tweak how grid filter params are built, for better link share -- allow passing filter factory to `Grid.set_filter()` -- track column-only labels separately for grid -- make view responsible for App Info field/values - ## v0.28.1 (2026-02-17) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 5b7eb80..ad847e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttaWeb" -version = "0.28.2" +version = "0.28.1" description = "Web App for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] diff --git a/src/wuttaweb/forms/base.py b/src/wuttaweb/forms/base.py index 796fd6f..b1e142a 100644 --- a/src/wuttaweb/forms/base.py +++ b/src/wuttaweb/forms/base.py @@ -1138,18 +1138,7 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth field = dform[fieldname] if readonly: kwargs["readonly"] = True - - try: - html = field.serialize(**kwargs) - except Exception as exc: - log.warning( - "widget serialization failed for field: %s", - fieldname, - exc_info=True, - ) - raise RuntimeError( - f"widget serialization failed for field: {fieldname}" - ) from exc + html = field.serialize(**kwargs) else: # render static text if field not in deform/schema diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py index 2ac2646..077f51a 100644 --- a/src/wuttaweb/grids/base.py +++ b/src/wuttaweb/grids/base.py @@ -119,17 +119,9 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth .. attribute:: labels - Dict of column and/or filter label overrides. + Dict of column label overrides. - See also :attr:`column_labels`, :meth:`set_label()`, - :meth:`get_column_label()` and :meth:`get_filter_label()`. - - .. attribute:: column_labels - - Dict of label overrides for column only. - - See also :attr:`labels`, :meth:`set_label()` and - :meth:`get_column_label()`. + See also :meth:`get_label()` and :meth:`set_label()`. .. attribute:: centered @@ -442,7 +434,6 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth self.key = key self.data = data self.labels = labels or {} - self.column_labels = {} self.checkable = checkable self.row_class = row_class self.actions = actions or [] @@ -637,61 +628,35 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth def set_label(self, key, label, column_only=False): """ - Set/override the label for a column and/or filter. + Set/override the label for a column. - :param key: Key for the column/filter. + :param key: Name of column. - :param label: New label for the column and/or filter. + :param label: New label for the column header. :param column_only: Boolean indicating whether the label should be applied *only* to the column header (if ``True``), vs. applying also to the filter (if ``False``). - See also :meth:`get_column_label()` and - :meth:`get_filter_label()`. Label overrides are tracked via - :attr:`labels` and :attr:`column_labels`. + See also :meth:`get_label()`. Label overrides are tracked via + :attr:`labels`. """ - if column_only: - self.column_labels[key] = label - else: - self.labels[key] = label - if key in self.filters: - self.filters[key].label = label + self.labels[key] = label - def get_label(self, key): # pylint: disable=missing-function-docstring - warnings.warn( - "Grid.get_label() is deprecated; please use " - "get_filter_label() or get_column_label() instead", - DeprecationWarning, - stacklevel=2, - ) - return self.get_filter_label(key) + if not column_only and key in self.filters: + self.filters[key].label = label - def get_filter_label(self, key): - """ - Returns the label text for a given filter. - - If no override is defined, the label is derived from ``key``. - - See also :meth:`set_label()` and :meth:`get_column_label()`. - """ - if key in self.labels: - return self.labels[key] - - return self.app.make_title(key) - - def get_column_label(self, key): + def get_label(self, key): """ Returns the label text for a given column. If no override is defined, the label is derived from ``key``. - See also :meth:`set_label()` and :meth:`get_filter_label()`. + See also :meth:`set_label()`. """ - if key in self.column_labels: - return self.column_labels[key] - - return self.get_filter_label(key) + if key in self.labels: + return self.labels[key] + return self.app.make_title(key) def set_centered(self, key, centered=True): """ @@ -1404,11 +1369,10 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth Code usually does not need to call this directly. See also :meth:`set_filter()`, which calls this method automatically. - :param columninfo: Can be either a model property - (e.g. ``model.User.username``), or a column name - (e.g. ``"username"``). + :param columninfo: Can be either a model property (see below), + or a column name. - :returns: :class:`~wuttaweb.grids.filters.GridFilter` + :returns: A :class:`~wuttaweb.grids.filters.GridFilter` instance. """ key = kwargs.pop("key", None) @@ -1446,18 +1410,12 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth :param key: Name of column. - :param filterinfo: Can be either a filter factory, or else a - model property (e.g. ``model.User.username``) or column - name (e.g. ``"username"``). If not specified then the - ``key`` will be used instead. + :param filterinfo: Can be either a + :class:`~wuttweb.grids.filters.GridFilter` instance, or + else a model property (see below). - :param \\**kwargs: Additional kwargs to pass along to the - filter factory. - - If ``filterinfo`` is a factory, it will be called with the - current request, key and kwargs like so:: - - filtr = factory(self.request, key, **kwargs) + If ``filterinfo`` is a ``GridFilter`` instance, it will be + used as-is for the backend filter. Otherwise :meth:`make_filter()` will be called to obtain the backend filter. The ``filterinfo`` will be passed along to @@ -1469,13 +1427,12 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth filtr = None if filterinfo and callable(filterinfo): - kwargs.setdefault("label", self.get_filter_label(key)) - filtr = filterinfo(self.request, key, **kwargs) + # filtr = filterinfo + raise NotImplementedError - else: - kwargs["key"] = key - kwargs.setdefault("label", self.get_filter_label(key)) - filtr = self.make_filter(filterinfo or key, **kwargs) + kwargs["key"] = key + kwargs.setdefault("label", self.get_label(key)) + filtr = self.make_filter(filterinfo or key, **kwargs) self.filters[key] = filtr @@ -2357,7 +2314,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth columns.append( { "field": name, - "label": self.get_column_label(name), + "label": self.get_label(name), "hidden": self.is_hidden(name), "sortable": self.is_sortable(name), "searchable": self.is_searchable(name), diff --git a/src/wuttaweb/grids/filters.py b/src/wuttaweb/grids/filters.py index f34d848..170fbd9 100644 --- a/src/wuttaweb/grids/filters.py +++ b/src/wuttaweb/grids/filters.py @@ -189,10 +189,6 @@ class GridFilter: # pylint: disable=too-many-instance-attributes self.app = self.config.get_app() self.label = label or self.app.make_title(self.key) - # remember original data type in case we need to revert, - # e.g. after changing it to 'choices' and back again - self.original_data_type = self.data_type - # active self.default_active = default_active self.active = self.default_active @@ -205,7 +201,7 @@ class GridFilter: # pylint: disable=too-many-instance-attributes self.verb = None # active verb is set later # choices - self.set_choices(choices) + self.set_choices(choices or {}) # nullable self.nullable = nullable @@ -308,8 +304,7 @@ class GridFilter: # pylint: disable=too-many-instance-attributes self.data_type = "choice" else: self.choices = {} - if self.data_type == "choice": - self.data_type = self.original_data_type + self.data_type = "string" def normalize_choices(self, choices): """ @@ -350,26 +345,6 @@ class GridFilter: # pylint: disable=too-many-instance-attributes return normalized - def coerce_value(self, value): - """ - Coerce the given value to the correct type/format for use with - the filter. This is where e.g. a boolean or date filter - should convert input string to ``bool`` or ``date`` value. - - This is (usually) called from a filter method, when applying - the filter. See also :meth:`apply_filter()`. - - Default logic on the base class returns value as-is; subclass - may override as needed. - - :param value: Input string provided by the user via the filter - form submission. - - :returns: Value of the appropriate type, depending on the - filter subclass. - """ - return value - def apply_filter(self, data, verb=None, value=UNSPECIFIED): """ Filter the given data set according to a verb/value pair. @@ -440,6 +415,15 @@ class AlchemyFilter(GridFilter): if len(columns) == 1: self.nullable = columns[0].nullable + def coerce_value(self, value): + """ + Coerce the given value to the correct type/format for use with + the filter. + + Default logic returns value as-is; subclass may override. + """ + return value + def filter_equal(self, query, value): """ Filter data with an equal (``=``) condition. diff --git a/src/wuttaweb/templates/appinfo/index.mako b/src/wuttaweb/templates/appinfo/index.mako index b7beb8a..d296389 100644 --- a/src/wuttaweb/templates/appinfo/index.mako +++ b/src/wuttaweb/templates/appinfo/index.mako @@ -7,11 +7,30 @@

Application

- % for key, info in (appinfo or {}).items(): - - ${info["value"]} - - % endfor + + ${app.get_distribution() or f'?? - set config for `{app.appname}.app_dist`'} + + + ${app.get_version() or f'?? - set config for `{app.appname}.app_dist`'} + + + ${app.get_title()} + + + ${app.get_node_title()} + + + ${config.appdb_engine.dialect.name} + + + ${app.get_timezone_name()} + + + ${"Yes" if config.production() else "No"} + + + ${"Yes" if app.get_email_handler().sending_is_enabled() else "No"} +
diff --git a/src/wuttaweb/templates/grids/vue_template.mako b/src/wuttaweb/templates/grids/vue_template.mako index 47720fa..d6e1398 100644 --- a/src/wuttaweb/templates/grids/vue_template.mako +++ b/src/wuttaweb/templates/grids/vue_template.mako @@ -653,21 +653,9 @@ params[filter.key+'.verb'] = filter.verb } } - - ## nb. we used to add filter=true only if some - ## filter(s) is currently active, but that can cause - ## problems when sharing a link for a grid which - ## *currently* has no active filters, but which does - ## have *default* filters. so for now we always - ## declare filters to be "in effect" even if there - ## are none active. hopefully that does not break - ## anything else but this note is here just in case. - - ## if (Object.keys(params).length) { - ## params.filter = 'true' - ## } - params.filter = 'true' - + if (Object.keys(params).length) { + params.filter = 'true' + } return params }, diff --git a/src/wuttaweb/templates/wutta-components.mako b/src/wuttaweb/templates/wutta-components.mako index e79c894..093121d 100644 --- a/src/wuttaweb/templates/wutta-components.mako +++ b/src/wuttaweb/templates/wutta-components.mako @@ -623,7 +623,7 @@ -