Compare commits
3 commits
91e10274ea
...
dce91a3a96
Author | SHA1 | Date | |
---|---|---|---|
dce91a3a96 | |||
1efaca4e52 | |||
4643aa3e3c |
|
@ -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,14 +462,13 @@ 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
|
mapper = sa.inspect(model_class)
|
||||||
if not issubclass(model_class, model.Base):
|
except sa.exc.NoInspectionAvailable:
|
||||||
return
|
pass
|
||||||
|
else:
|
||||||
mapper = sa.inspect(model_class)
|
fields = [prop.key for prop in mapper.iterate_properties]
|
||||||
fields = [prop.key for prop in mapper.iterate_properties]
|
return fields
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
def make_json_safe(value, key=None, warn=True):
|
def make_json_safe(value, key=None, warn=True):
|
||||||
|
|
|
@ -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…
Reference in a new issue