diff --git a/pyproject.toml b/pyproject.toml
index e07c98b..9570746 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,6 +31,7 @@ classifiers = [
requires-python = ">= 3.8"
dependencies = [
"ColanderAlchemy",
+ "paginate",
"pyramid>=2",
"pyramid_beaker",
"pyramid_deform",
diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py
index 740607c..5f5b34e 100644
--- a/src/wuttaweb/grids/base.py
+++ b/src/wuttaweb/grids/base.py
@@ -61,6 +61,11 @@ class Grid:
Presumably unique key for the grid; used to track per-grid
sort/filter settings etc.
+ .. attribute:: vue_tagname
+
+ String name for Vue component tag. By default this is
+ ``'wutta-grid'``. See also :meth:`render_vue_tag()`.
+
.. attribute:: model_class
Model class for the grid, if applicable. When set, this is
@@ -106,15 +111,43 @@ class Grid:
See also :meth:`set_link()` and :meth:`is_linked()`.
- .. attribute:: vue_tagname
+ .. attribute:: paginated
- String name for Vue component tag. By default this is
- ``'wutta-grid'``. See also :meth:`render_vue_tag()`.
+ Boolean indicating whether the grid data should be paginated
+ vs. all data is shown at once. Default is ``False``.
+
+ See also :attr:`pagesize` and :attr:`page`.
+
+ .. attribute:: pagesize_options
+
+ List of "page size" options for the grid. See also
+ :attr:`pagesize`.
+
+ Only relevant if :attr:`paginated` is true. If not specified,
+ constructor will call :meth:`get_pagesize_options()` to get the
+ value.
+
+ .. attribute:: pagesize
+
+ Number of records to show in a data page. See also
+ :attr:`pagesize_options` and :attr:`page`.
+
+ Only relevant if :attr:`paginated` is true. If not specified,
+ constructor will call :meth:`get_pagesize()` to get the value.
+
+ .. attribute:: page
+
+ The current page number (of data) to display in the grid. See
+ also :attr:`pagesize`.
+
+ Only relevant if :attr:`paginated` is true. If not specified,
+ constructor will assume ``1`` (first page).
"""
def __init__(
self,
request,
+ vue_tagname='wutta-grid',
model_class=None,
key=None,
columns=None,
@@ -123,9 +156,13 @@ class Grid:
renderers={},
actions=[],
linked_columns=[],
- vue_tagname='wutta-grid',
+ paginated=False,
+ pagesize_options=None,
+ pagesize=None,
+ page=1,
):
self.request = request
+ self.vue_tagname = vue_tagname
self.model_class = model_class
self.key = key
self.data = data
@@ -133,13 +170,17 @@ class Grid:
self.renderers = renderers or {}
self.actions = actions or []
self.linked_columns = linked_columns or []
- self.vue_tagname = vue_tagname
self.config = self.request.wutta_config
self.app = self.config.get_app()
self.set_columns(columns or self.get_columns())
+ self.paginated = paginated
+ self.pagesize_options = pagesize_options or self.get_pagesize_options()
+ self.pagesize = pagesize or self.get_pagesize()
+ self.page = page
+
def get_columns(self):
"""
Returns the official list of column names for the grid, or
@@ -340,6 +381,64 @@ class Grid:
return True
return False
+ ##############################
+ # paging methods
+ ##############################
+
+ def get_pagesize_options(self, default=None):
+ """
+ Returns a list of default page size options for the grid.
+
+ It will check config but if no setting exists, will fall
+ back to::
+
+ [5, 10, 20, 50, 100, 200]
+
+ :param default: Alternate default value to return if none is
+ configured.
+
+ This method is intended for use in the constructor. Code can
+ instead access :attr:`pagesize_options` directly.
+ """
+ options = self.config.get_list('wuttaweb.grids.default_pagesize_options')
+ if options:
+ options = [int(size) for size in options
+ if size.isdigit()]
+ if options:
+ return options
+
+ return default or [5, 10, 20, 50, 100, 200]
+
+ def get_pagesize(self, default=None):
+ """
+ Returns the default page size for the grid.
+
+ It will check config but if no setting exists, will fall back
+ to a value from :attr:`pagesize_options` (will return ``20`` if
+ that is listed; otherwise the "first" option).
+
+ :param default: Alternate default value to return if none is
+ configured.
+
+ This method is intended for use in the constructor. Code can
+ instead access :attr:`pagesize` directly.
+ """
+ size = self.config.get_int('wuttaweb.grids.default_pagesize')
+ if size:
+ return size
+
+ if default:
+ return default
+
+ if 20 in self.pagesize_options:
+ return 20
+
+ return self.pagesize_options[0]
+
+ ##############################
+ # rendering methods
+ ##############################
+
def render_vue_tag(self, **kwargs):
"""
Render the Vue component tag for the grid.
diff --git a/src/wuttaweb/templates/grids/vue_template.mako b/src/wuttaweb/templates/grids/vue_template.mako
index 5e60bab..0505c33 100644
--- a/src/wuttaweb/templates/grids/vue_template.mako
+++ b/src/wuttaweb/templates/grids/vue_template.mako
@@ -2,8 +2,20 @@
diff --git a/src/wuttaweb/views/master.py b/src/wuttaweb/views/master.py
index 1c7518d..0fb050d 100644
--- a/src/wuttaweb/views/master.py
+++ b/src/wuttaweb/views/master.py
@@ -181,6 +181,14 @@ class MasterView(View):
This is optional; see also :meth:`get_grid_columns()`.
+ .. attribute:: paginated
+
+ Boolean indicating whether the grid data for the
+ :meth:`index()` view should be paginated. Default is ``True``.
+
+ This is used by :meth:`make_model_grid()` to set the grid's
+ :attr:`~wuttaweb.grids.base.Grid.paginated` flag.
+
.. attribute:: creatable
Boolean indicating whether the view model supports "creating" -
@@ -229,6 +237,7 @@ class MasterView(View):
# features
listable = True
has_grid = True
+ paginated = True
creatable = True
viewable = True
editable = True
@@ -1061,6 +1070,8 @@ class MasterView(View):
kwargs['actions'] = actions
+ kwargs.setdefault('paginated', self.paginated)
+
grid = self.make_grid(**kwargs)
self.configure_grid(grid)
return grid
diff --git a/tests/grids/test_base.py b/tests/grids/test_base.py
index 2d15689..9e23c02 100644
--- a/tests/grids/test_base.py
+++ b/tests/grids/test_base.py
@@ -8,24 +8,10 @@ from pyramid import testing
from wuttjamaican.conf import WuttaConfig
from wuttaweb.grids import base
from wuttaweb.forms import FieldList
+from tests.util import WebTestCase
-class TestGrid(TestCase):
-
- def setUp(self):
- self.config = WuttaConfig(defaults={
- 'wutta.web.menus.handler_spec': 'tests.util:NullMenuHandler',
- })
- self.app = self.config.get_app()
-
- self.request = testing.DummyRequest(wutta_config=self.config, use_oruga=False)
-
- self.pyramid_config = testing.setUp(request=self.request, settings={
- 'mako.directories': ['wuttaweb:templates'],
- })
-
- def tearDown(self):
- testing.tearDown()
+class TestGrid(WebTestCase):
def make_grid(self, request=None, **kwargs):
return base.Grid(request or self.request, **kwargs)
@@ -144,6 +130,44 @@ class TestGrid(TestCase):
self.assertFalse(grid.is_linked('foo'))
self.assertTrue(grid.is_linked('bar'))
+ def test_get_pagesize_options(self):
+ grid = self.make_grid()
+
+ # default
+ options = grid.get_pagesize_options()
+ self.assertEqual(options, [5, 10, 20, 50, 100, 200])
+
+ # override default
+ options = grid.get_pagesize_options(default=[42])
+ self.assertEqual(options, [42])
+
+ # from config
+ self.config.setdefault('wuttaweb.grids.default_pagesize_options', '1 2 3')
+ options = grid.get_pagesize_options()
+ self.assertEqual(options, [1, 2, 3])
+
+ def test_get_pagesize(self):
+ grid = self.make_grid()
+
+ # default
+ size = grid.get_pagesize()
+ self.assertEqual(size, 20)
+
+ # override default
+ size = grid.get_pagesize(default=42)
+ self.assertEqual(size, 42)
+
+ # override default options
+ self.config.setdefault('wuttaweb.grids.default_pagesize_options', '10 15 30')
+ grid = self.make_grid()
+ size = grid.get_pagesize()
+ self.assertEqual(size, 10)
+
+ # from config
+ self.config.setdefault('wuttaweb.grids.default_pagesize', '15')
+ size = grid.get_pagesize()
+ self.assertEqual(size, 15)
+
def test_render_vue_tag(self):
grid = self.make_grid(columns=['foo', 'bar'])
html = grid.render_vue_tag()