feat: add basic MasterView to show all registered master views
also serves as anchor for "make new master view" feature, coming soon
This commit is contained in:
parent
21775257a9
commit
92d4ce43b1
8 changed files with 222 additions and 0 deletions
6
docs/api/wuttaweb.views.views.rst
Normal file
6
docs/api/wuttaweb.views.views.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb.views.views``
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb.views.views
|
||||||
|
:members:
|
||||||
|
|
@ -74,6 +74,7 @@ the narrative docs are pretty scant. That will eventually change.
|
||||||
api/wuttaweb.views.tables
|
api/wuttaweb.views.tables
|
||||||
api/wuttaweb.views.upgrades
|
api/wuttaweb.views.upgrades
|
||||||
api/wuttaweb.views.users
|
api/wuttaweb.views.users
|
||||||
|
api/wuttaweb.views.views
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,14 @@
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
|
||||||
|
% if request.has_perm("master_views.list"):
|
||||||
|
<wutta-button type="is-primary"
|
||||||
|
tag="a" href="${url('master_views')}"
|
||||||
|
icon-left="eye"
|
||||||
|
label="Master Views"
|
||||||
|
once />
|
||||||
|
% endif
|
||||||
|
|
||||||
% if request.has_perm("app_tables.list"):
|
% if request.has_perm("app_tables.list"):
|
||||||
<wutta-button type="is-primary"
|
<wutta-button type="is-primary"
|
||||||
tag="a" href="${url('app_tables')}"
|
tag="a" href="${url('app_tables')}"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,14 @@
|
||||||
once />
|
once />
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm("master_views.list"):
|
||||||
|
<wutta-button type="is-primary"
|
||||||
|
tag="a" href="${url('master_views')}"
|
||||||
|
icon-left="eye"
|
||||||
|
label="Master Views"
|
||||||
|
once />
|
||||||
|
% endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
${parent.page_content()}
|
${parent.page_content()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
||||||
17
src/wuttaweb/templates/views/master/index.mako
Normal file
17
src/wuttaweb/templates/views/master/index.mako
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/index.mako" />
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
<div class="buttons">
|
||||||
|
|
||||||
|
% if request.has_perm("app_tables.list"):
|
||||||
|
<wutta-button type="is-primary"
|
||||||
|
tag="a" href="${url('app_tables')}"
|
||||||
|
icon-left="table"
|
||||||
|
label="App Tables"
|
||||||
|
once />
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
${parent.page_content()}
|
||||||
|
</%def>
|
||||||
|
|
@ -40,6 +40,7 @@ That will in turn include the following modules:
|
||||||
* :mod:`wuttaweb.views.upgrades`
|
* :mod:`wuttaweb.views.upgrades`
|
||||||
* :mod:`wuttaweb.views.tables`
|
* :mod:`wuttaweb.views.tables`
|
||||||
* :mod:`wuttaweb.views.alembic`
|
* :mod:`wuttaweb.views.alembic`
|
||||||
|
* :mod:`wuttaweb.views.views`
|
||||||
|
|
||||||
You can also selectively override some modules while keeping most
|
You can also selectively override some modules while keeping most
|
||||||
defaults.
|
defaults.
|
||||||
|
|
@ -77,6 +78,7 @@ def defaults(config, **kwargs): # pylint: disable=missing-function-docstring
|
||||||
config.include(mod("wuttaweb.views.upgrades"))
|
config.include(mod("wuttaweb.views.upgrades"))
|
||||||
config.include(mod("wuttaweb.views.tables"))
|
config.include(mod("wuttaweb.views.tables"))
|
||||||
config.include(mod("wuttaweb.views.alembic"))
|
config.include(mod("wuttaweb.views.alembic"))
|
||||||
|
config.include(mod("wuttaweb.views.views"))
|
||||||
|
|
||||||
|
|
||||||
def includeme(config): # pylint: disable=missing-function-docstring
|
def includeme(config): # pylint: disable=missing-function-docstring
|
||||||
|
|
|
||||||
134
src/wuttaweb/views/views.py
Normal file
134
src/wuttaweb/views/views.py
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# wuttaweb -- Web App for Wutta Framework
|
||||||
|
# Copyright © 2024-2025 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Wutta Framework.
|
||||||
|
#
|
||||||
|
# Wutta Framework is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
#
|
||||||
|
# Wutta Framework is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Views of Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from wuttaweb.views import MasterView
|
||||||
|
|
||||||
|
|
||||||
|
class MasterViewView(MasterView): # pylint: disable=abstract-method
|
||||||
|
"""
|
||||||
|
Master view which shows a list of all master views found in the
|
||||||
|
app registry.
|
||||||
|
|
||||||
|
Route prefix is ``master_views``; notable URLs provided by this
|
||||||
|
class include:
|
||||||
|
|
||||||
|
* ``/views/master/``
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_name = "master_view"
|
||||||
|
model_title = "Master View"
|
||||||
|
model_title_plural = "Master Views"
|
||||||
|
url_prefix = "/views/master"
|
||||||
|
|
||||||
|
filterable = False
|
||||||
|
sortable = True
|
||||||
|
sort_on_backend = False
|
||||||
|
paginated = True
|
||||||
|
paginate_on_backend = False
|
||||||
|
|
||||||
|
creatable = False
|
||||||
|
viewable = False # nb. it has a pseudo-view action instead
|
||||||
|
editable = False
|
||||||
|
deletable = False
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"model_title_plural": "Title",
|
||||||
|
"url_prefix": "URL Prefix",
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_columns = [
|
||||||
|
"model_title_plural",
|
||||||
|
"model_name",
|
||||||
|
"route_prefix",
|
||||||
|
"url_prefix",
|
||||||
|
]
|
||||||
|
|
||||||
|
sort_defaults = "model_title_plural"
|
||||||
|
|
||||||
|
def get_grid_data( # pylint: disable=empty-docstring
|
||||||
|
self, columns=None, session=None
|
||||||
|
):
|
||||||
|
""" """
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# nb. we do not omit any views due to lack of permission here.
|
||||||
|
# all views are shown for anyone seeing this page. this is
|
||||||
|
# for sake of clarity so admin users are aware of what is
|
||||||
|
# *possible* within the app etc.
|
||||||
|
master_views = self.request.registry.settings.get("wuttaweb_master_views", {})
|
||||||
|
for model_views in master_views.values():
|
||||||
|
for view in model_views:
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"model_title_plural": view.get_model_title_plural(),
|
||||||
|
"model_name": view.get_model_name(),
|
||||||
|
"route_prefix": view.get_route_prefix(),
|
||||||
|
"url_prefix": view.get_url_prefix(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def configure_grid(self, grid): # pylint: disable=empty-docstring
|
||||||
|
""" """
|
||||||
|
g = grid
|
||||||
|
super().configure_grid(g)
|
||||||
|
|
||||||
|
# nb. show more views by default
|
||||||
|
g.pagesize = 50
|
||||||
|
|
||||||
|
# nb. add "pseudo" View action
|
||||||
|
def viewurl(view, i): # pylint: disable=unused-argument
|
||||||
|
return self.request.route_url(view["route_prefix"])
|
||||||
|
|
||||||
|
g.add_action("view", icon="eye", url=viewurl)
|
||||||
|
|
||||||
|
# model_title_plural
|
||||||
|
g.set_link("model_title_plural")
|
||||||
|
g.set_searchable("model_title_plural")
|
||||||
|
|
||||||
|
# model_name
|
||||||
|
g.set_searchable("model_name")
|
||||||
|
|
||||||
|
# route_prefix
|
||||||
|
g.set_searchable("route_prefix")
|
||||||
|
|
||||||
|
# url_prefix
|
||||||
|
g.set_link("url_prefix")
|
||||||
|
g.set_searchable("url_prefix")
|
||||||
|
|
||||||
|
|
||||||
|
def defaults(config, **kwargs): # pylint: disable=missing-function-docstring
|
||||||
|
base = globals()
|
||||||
|
|
||||||
|
MasterViewView = kwargs.get( # pylint: disable=invalid-name,redefined-outer-name
|
||||||
|
"MasterViewView", base["MasterViewView"]
|
||||||
|
)
|
||||||
|
MasterViewView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config): # pylint: disable=missing-function-docstring
|
||||||
|
defaults(config)
|
||||||
46
tests/views/test_views.py
Normal file
46
tests/views/test_views.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from wuttaweb.testing import WebTestCase
|
||||||
|
from wuttaweb.views import views as mod
|
||||||
|
from wuttaweb.views.users import UserView
|
||||||
|
|
||||||
|
|
||||||
|
class TestMasterViewView(WebTestCase):
|
||||||
|
|
||||||
|
def make_view(self):
|
||||||
|
return mod.MasterViewView(self.request)
|
||||||
|
|
||||||
|
def test_includeme(self):
|
||||||
|
self.pyramid_config.include("wuttaweb.views.views")
|
||||||
|
|
||||||
|
def test_get_grid_data(self):
|
||||||
|
view = self.make_view()
|
||||||
|
|
||||||
|
# empty by default, since nothing registered in test setup
|
||||||
|
data = view.get_grid_data()
|
||||||
|
self.assertIsInstance(data, list)
|
||||||
|
self.assertEqual(len(data), 0)
|
||||||
|
|
||||||
|
# so let's register one and try again
|
||||||
|
self.pyramid_config.add_wutta_master_view(UserView)
|
||||||
|
data = view.get_grid_data()
|
||||||
|
self.assertGreater(len(data), 0)
|
||||||
|
master = data[0]
|
||||||
|
self.assertIsInstance(master, dict)
|
||||||
|
self.assertEqual(master["model_title_plural"], "Users")
|
||||||
|
self.assertEqual(master["model_name"], "User")
|
||||||
|
self.assertEqual(master["url_prefix"], "/users")
|
||||||
|
|
||||||
|
def test_configure_grid(self):
|
||||||
|
self.pyramid_config.add_route("users", "/users/")
|
||||||
|
self.pyramid_config.add_wutta_master_view(UserView)
|
||||||
|
view = self.make_view()
|
||||||
|
|
||||||
|
# sanity / coverage check
|
||||||
|
grid = view.make_grid(
|
||||||
|
columns=["model_title_plural", "url_prefix"], data=view.get_grid_data()
|
||||||
|
)
|
||||||
|
view.configure_grid(grid)
|
||||||
|
|
||||||
|
# nb. must invoke this to exercise the url logic
|
||||||
|
grid.get_vue_context()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue