feat: add basic support for rows grid for master, batch views
This commit is contained in:
parent
5006c97b4b
commit
e3beb9953d
|
@ -5,5 +5,34 @@
|
|||
|
||||
<%def name="content_title()">${instance_title}</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
|
||||
${parent.body()}
|
||||
## render main form
|
||||
${parent.page_content()}
|
||||
|
||||
## render row grid
|
||||
% if master.has_rows:
|
||||
<br />
|
||||
<h4 class="block is-size-4">${master.get_rows_title() or ''}</h4>
|
||||
${rows_grid.render_vue_tag()}
|
||||
% endif
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if master.has_rows:
|
||||
${self.render_vue_template_rows_grid()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_template_rows_grid()">
|
||||
${rows_grid.render_vue_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if master.has_rows:
|
||||
${rows_grid.render_vue_finalize()}
|
||||
% endif
|
||||
</%def>
|
||||
|
|
|
@ -50,6 +50,10 @@ class BatchMasterView(MasterView):
|
|||
|
||||
sort_defaults = ('id', 'desc')
|
||||
|
||||
has_rows = True
|
||||
rows_title = "Batch Rows"
|
||||
rows_sort_defaults = 'sequence'
|
||||
|
||||
def __init__(self, request, context=None):
|
||||
super().__init__(request, context=context)
|
||||
self.batch_handler = self.get_batch_handler()
|
||||
|
@ -253,3 +257,33 @@ class BatchMasterView(MasterView):
|
|||
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
##############################
|
||||
# row methods
|
||||
##############################
|
||||
|
||||
@classmethod
|
||||
def get_row_model_class(cls):
|
||||
""" """
|
||||
if hasattr(cls, 'row_model_class'):
|
||||
return cls.row_model_class
|
||||
|
||||
Batch = cls.get_model_class()
|
||||
return Batch.__row_class__
|
||||
|
||||
def get_row_grid_data(self, batch):
|
||||
"""
|
||||
Returns the base query for the batch
|
||||
:attr:`~wuttjamaican:wuttjamaican.db.model.batch.BatchMixin.rows`
|
||||
data.
|
||||
"""
|
||||
BatchRow = self.get_row_model_class()
|
||||
query = self.Session.query(BatchRow)\
|
||||
.filter(BatchRow.batch == batch)
|
||||
return query
|
||||
|
||||
def configure_row_grid(self, g):
|
||||
""" """
|
||||
super().configure_row_grid(g)
|
||||
|
||||
g.set_label('sequence', "Seq.", column_only=True)
|
||||
|
|
|
@ -73,12 +73,12 @@ class MasterView(View):
|
|||
|
||||
.. attribute:: model_class
|
||||
|
||||
Optional reference to a data model class. While not strictly
|
||||
required, most views will set this to a SQLAlchemy mapped
|
||||
class,
|
||||
Optional reference to a :term:`data model` class. While not
|
||||
strictly required, most views will set this to a SQLAlchemy
|
||||
mapped class,
|
||||
e.g. :class:`~wuttjamaican:wuttjamaican.db.model.base.Person`.
|
||||
|
||||
Code should not access this directly but instead call
|
||||
The base logic should not access this directly but instead call
|
||||
:meth:`get_model_class()`.
|
||||
|
||||
.. attribute:: model_name
|
||||
|
@ -340,6 +340,38 @@ class MasterView(View):
|
|||
Boolean indicating whether the master view supports
|
||||
"configuring" - i.e. it should have a :meth:`configure()` view.
|
||||
Default value is ``False``.
|
||||
|
||||
**ROW FEATURES**
|
||||
|
||||
.. attribute:: has_rows
|
||||
|
||||
Whether the model has "rows" which should also be displayed
|
||||
when viewing model records.
|
||||
|
||||
This the "master switch" for all row features; if this is turned
|
||||
on then many other things kick in.
|
||||
|
||||
See also :attr:`row_model_class`.
|
||||
|
||||
.. attribute:: row_model_class
|
||||
|
||||
Reference to a :term:`data model` class for the rows.
|
||||
|
||||
The base logic should not access this directly but instead call
|
||||
:meth:`get_row_model_class()`.
|
||||
|
||||
.. attribute:: rows_title
|
||||
|
||||
Display title for the rows grid.
|
||||
|
||||
The base logic should not access this directly but instead call
|
||||
:meth:`get_rows_title()`.
|
||||
|
||||
.. attribute:: row_grid_columns
|
||||
|
||||
List of columns for the row grid.
|
||||
|
||||
This is optional; see also :meth:`get_row_grid_columns()`.
|
||||
"""
|
||||
|
||||
##############################
|
||||
|
@ -368,6 +400,16 @@ class MasterView(View):
|
|||
execute_progress_template = None
|
||||
configurable = False
|
||||
|
||||
# row features
|
||||
has_rows = False
|
||||
rows_filterable = True
|
||||
rows_filter_defaults = None
|
||||
rows_sortable = True
|
||||
rows_sort_on_backend = True
|
||||
rows_sort_defaults = None
|
||||
rows_paginated = True
|
||||
rows_paginate_on_backend = True
|
||||
|
||||
# current action
|
||||
listing = False
|
||||
creating = False
|
||||
|
@ -525,15 +567,40 @@ class MasterView(View):
|
|||
|
||||
* :meth:`make_model_form()`
|
||||
* :meth:`configure_form()`
|
||||
* :meth:`make_row_model_grid()` - if :attr:`has_rows` is true
|
||||
"""
|
||||
self.viewing = True
|
||||
instance = self.get_instance()
|
||||
form = self.make_model_form(instance, readonly=True)
|
||||
|
||||
obj = self.get_instance()
|
||||
form = self.make_model_form(obj, readonly=True)
|
||||
context = {
|
||||
'instance': instance,
|
||||
'instance': obj,
|
||||
'form': form,
|
||||
}
|
||||
|
||||
if self.has_rows:
|
||||
|
||||
# always make the grid first. note that it already knows
|
||||
# to "reset" its params when that is requested.
|
||||
grid = self.make_row_model_grid(obj)
|
||||
|
||||
# but if user did request a "reset" then we want to
|
||||
# redirect so the query string gets cleared out
|
||||
if self.request.GET.get('reset-view'):
|
||||
|
||||
# nb. we want to preserve url hash if applicable
|
||||
kw = {'_query': None,
|
||||
'_anchor': self.request.GET.get('hash')}
|
||||
return self.redirect(self.request.current_route_url(**kw))
|
||||
|
||||
# so-called 'partial' requests get just the grid data
|
||||
if self.request.params.get('partial'):
|
||||
context = grid.get_vue_context()
|
||||
if grid.paginated and grid.paginate_on_backend:
|
||||
context['pager_stats'] = grid.get_vue_pager_stats()
|
||||
return self.json_response(context)
|
||||
|
||||
context['rows_grid'] = grid
|
||||
|
||||
return self.render_to_response('view', context)
|
||||
|
||||
##############################
|
||||
|
@ -1907,8 +1974,8 @@ class MasterView(View):
|
|||
|
||||
This is called by :meth:`make_model_grid()`.
|
||||
|
||||
There is no default logic here; subclass should override as
|
||||
needed. The ``grid`` param will already be "complete" and
|
||||
There is minimal default logic here; subclass should override
|
||||
as needed. The ``grid`` param will already be "complete" and
|
||||
ready to use as-is, but this method can further modify it
|
||||
based on request details etc.
|
||||
"""
|
||||
|
@ -2241,6 +2308,182 @@ class MasterView(View):
|
|||
session = session or self.Session()
|
||||
session.add(obj)
|
||||
|
||||
##############################
|
||||
# row methods
|
||||
##############################
|
||||
|
||||
def get_rows_title(self):
|
||||
"""
|
||||
Returns the display title for model **rows** grid, if
|
||||
applicable/desired. Only relevant if :attr:`has_rows` is
|
||||
true.
|
||||
|
||||
There is no default here, but subclass may override by
|
||||
assigning :attr:`rows_title`.
|
||||
"""
|
||||
if hasattr(self, 'rows_title'):
|
||||
return self.rows_title
|
||||
|
||||
def make_row_model_grid(self, obj, **kwargs):
|
||||
"""
|
||||
Create and return a grid for a record's **rows** data, for use
|
||||
in :meth:`view()`. Only applicable if :attr:`has_rows` is
|
||||
true.
|
||||
|
||||
:param obj: Current model instance for which rows data is
|
||||
being displayed.
|
||||
|
||||
:returns: :class:`~wuttaweb.grids.base.Grid` instance for the
|
||||
rows data.
|
||||
|
||||
See also related methods, which are called by this one:
|
||||
|
||||
* :meth:`get_row_grid_key()`
|
||||
* :meth:`get_row_grid_columns()`
|
||||
* :meth:`get_row_grid_data()`
|
||||
* :meth:`configure_row_grid()`
|
||||
"""
|
||||
if 'key' not in kwargs:
|
||||
kwargs['key'] = self.get_row_grid_key()
|
||||
|
||||
if 'model_class' not in kwargs:
|
||||
model_class = self.get_row_model_class()
|
||||
if model_class:
|
||||
kwargs['model_class'] = model_class
|
||||
|
||||
if 'columns' not in kwargs:
|
||||
kwargs['columns'] = self.get_row_grid_columns()
|
||||
|
||||
if 'data' not in kwargs:
|
||||
kwargs['data'] = self.get_row_grid_data(obj)
|
||||
|
||||
kwargs.setdefault('filterable', self.rows_filterable)
|
||||
kwargs.setdefault('filter_defaults', self.rows_filter_defaults)
|
||||
kwargs.setdefault('sortable', self.rows_sortable)
|
||||
kwargs.setdefault('sort_multiple', not self.request.use_oruga)
|
||||
kwargs.setdefault('sort_on_backend', self.rows_sort_on_backend)
|
||||
kwargs.setdefault('sort_defaults', self.rows_sort_defaults)
|
||||
kwargs.setdefault('paginated', self.rows_paginated)
|
||||
kwargs.setdefault('paginate_on_backend', self.rows_paginate_on_backend)
|
||||
|
||||
grid = self.make_grid(**kwargs)
|
||||
self.configure_row_grid(grid)
|
||||
grid.load_settings()
|
||||
return grid
|
||||
|
||||
def get_row_grid_key(self):
|
||||
"""
|
||||
Returns the (presumably) unique key to be used for the
|
||||
**rows** grid in :meth:`view()`. Only relevant if
|
||||
:attr:`has_rows` is true.
|
||||
|
||||
This is called from :meth:`make_row_model_grid()`; in the
|
||||
resulting grid, this becomes
|
||||
:attr:`~wuttaweb.grids.base.Grid.key`.
|
||||
|
||||
Whereas you can define :attr:`grid_key` for the main grid, the
|
||||
row grid key is always generated dynamically. This
|
||||
incorporates the current record key (whose rows are in the
|
||||
grid) so that the rows grid for each record is unique.
|
||||
"""
|
||||
parts = [self.get_grid_key()]
|
||||
for key in self.get_model_key():
|
||||
parts.append(str(self.request.matchdict[key]))
|
||||
return '.'.join(parts)
|
||||
|
||||
def get_row_grid_columns(self):
|
||||
"""
|
||||
Returns the default list of column names for the **rows**
|
||||
grid, for use in :meth:`view()`. Only relevant if
|
||||
:attr:`has_rows` is true.
|
||||
|
||||
This is called by :meth:`make_row_model_grid()`; in the
|
||||
resulting grid, this becomes
|
||||
:attr:`~wuttaweb.grids.base.Grid.columns`.
|
||||
|
||||
This method may return ``None``, in which case the grid may
|
||||
(try to) generate its own default list.
|
||||
|
||||
Subclass may define :attr:`row_grid_columns` for simple cases,
|
||||
or can override this method if needed.
|
||||
|
||||
Also note that :meth:`configure_row_grid()` may be used to
|
||||
further modify the final column set, regardless of what this
|
||||
method returns. So a common pattern is to declare all
|
||||
"supported" columns by setting :attr:`row_grid_columns` but
|
||||
then optionally remove or replace some of those within
|
||||
:meth:`configure_row_grid()`.
|
||||
"""
|
||||
if hasattr(self, 'row_grid_columns'):
|
||||
return self.row_grid_columns
|
||||
|
||||
def get_row_grid_data(self, obj):
|
||||
"""
|
||||
Returns the data for the **rows** grid, for use in
|
||||
:meth:`view()`. Only relevant if :attr:`has_rows` is true.
|
||||
|
||||
This is called by :meth:`make_row_model_grid()`; in the
|
||||
resulting grid, this becomes
|
||||
:attr:`~wuttaweb.grids.base.Grid.data`.
|
||||
|
||||
Default logic not implemented; subclass must define this.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def configure_row_grid(self, grid):
|
||||
"""
|
||||
Configure the **rows** grid for use in :meth:`view()`. Only
|
||||
relevant if :attr:`has_rows` is true.
|
||||
|
||||
This is called by :meth:`make_row_model_grid()`.
|
||||
|
||||
There is minimal default logic here; subclass should override
|
||||
as needed. The ``grid`` param will already be "complete" and
|
||||
ready to use as-is, but this method can further modify it
|
||||
based on request details etc.
|
||||
"""
|
||||
grid.remove('uuid')
|
||||
self.set_row_labels(grid)
|
||||
|
||||
def set_row_labels(self, obj):
|
||||
"""
|
||||
Set label overrides on a **row** form or grid, based on what
|
||||
is defined by the view class and its parent class(es).
|
||||
|
||||
This is called automatically from
|
||||
:meth:`configure_row_grid()` and
|
||||
:meth:`configure_row_form()`.
|
||||
|
||||
This calls :meth:`collect_row_labels()` to find everything,
|
||||
then it assigns the labels using one of (based on ``obj``
|
||||
type):
|
||||
|
||||
* :func:`wuttaweb.forms.base.Form.set_label()`
|
||||
* :func:`wuttaweb.grids.base.Grid.set_label()`
|
||||
|
||||
:param obj: Either a :class:`~wuttaweb.grids.base.Grid` or a
|
||||
:class:`~wuttaweb.forms.base.Form` instance.
|
||||
"""
|
||||
labels = self.collect_row_labels()
|
||||
for key, label in labels.items():
|
||||
obj.set_label(key, label)
|
||||
|
||||
def collect_row_labels(self):
|
||||
"""
|
||||
Collect all **row** labels defined within the view class
|
||||
hierarchy.
|
||||
|
||||
This is called by :meth:`set_row_labels()`.
|
||||
|
||||
:returns: Dict of all labels found.
|
||||
"""
|
||||
labels = {}
|
||||
hierarchy = self.get_class_hierarchy()
|
||||
for cls in hierarchy:
|
||||
if hasattr(cls, 'row_labels'):
|
||||
labels.update(cls.row_labels)
|
||||
return labels
|
||||
|
||||
##############################
|
||||
# class methods
|
||||
##############################
|
||||
|
@ -2526,6 +2769,18 @@ class MasterView(View):
|
|||
|
||||
return cls.get_model_title_plural()
|
||||
|
||||
@classmethod
|
||||
def get_row_model_class(cls):
|
||||
"""
|
||||
Returns the **row** model class for the view, if defined.
|
||||
Only relevant if :attr:`has_rows` is true.
|
||||
|
||||
There is no default here, but a subclass may override by
|
||||
assigning :attr:`row_model_class`.
|
||||
"""
|
||||
if hasattr(cls, 'row_model_class'):
|
||||
return cls.row_model_class
|
||||
|
||||
##############################
|
||||
# configuration
|
||||
##############################
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import datetime
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from sqlalchemy import orm
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
|
||||
from wuttjamaican.db import model
|
||||
|
@ -19,12 +20,23 @@ class MockBatchRow(model.BatchRowMixin, model.Base):
|
|||
__tablename__ = 'testing_batch_mock_row'
|
||||
__batch_class__ = MockBatch
|
||||
|
||||
MockBatch.__row_class__ = MockBatchRow
|
||||
|
||||
class MockBatchHandler(BatchHandler):
|
||||
model_class = MockBatch
|
||||
|
||||
|
||||
class TestBatchMasterView(WebTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_web()
|
||||
|
||||
# nb. create MockBatch, MockBatchRow
|
||||
model.Base.metadata.create_all(bind=self.session.bind)
|
||||
|
||||
def make_view(self):
|
||||
return mod.BatchMasterView(self.request)
|
||||
|
||||
def test_get_batch_handler(self):
|
||||
self.assertRaises(NotImplementedError, mod.BatchMasterView, self.request)
|
||||
|
||||
|
@ -200,3 +212,78 @@ class TestBatchMasterView(WebTestCase):
|
|||
self.session.commit()
|
||||
# nb. should give up waiting after 1 second
|
||||
self.assertRaises(RuntimeError, view.populate_thread, batch.uuid)
|
||||
|
||||
def test_get_row_model_class(self):
|
||||
handler = MockBatchHandler(self.config)
|
||||
with patch.object(mod.BatchMasterView, 'get_batch_handler', return_value=handler):
|
||||
view = self.make_view()
|
||||
|
||||
self.assertRaises(AttributeError, view.get_row_model_class)
|
||||
|
||||
# row class determined from batch class
|
||||
with patch.object(mod.BatchMasterView, 'model_class', new=MockBatch, create=True):
|
||||
cls = view.get_row_model_class()
|
||||
self.assertIs(cls, MockBatchRow)
|
||||
|
||||
self.assertRaises(AttributeError, view.get_row_model_class)
|
||||
|
||||
# view may specify row class
|
||||
with patch.object(mod.BatchMasterView, 'row_model_class', new=MockBatchRow, create=True):
|
||||
cls = view.get_row_model_class()
|
||||
self.assertIs(cls, MockBatchRow)
|
||||
|
||||
def test_get_row_grid_data(self):
|
||||
handler = MockBatchHandler(self.config)
|
||||
model = self.app.model
|
||||
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
|
||||
batch = handler.make_batch(self.session, created_by=user)
|
||||
self.session.add(batch)
|
||||
row = handler.make_row()
|
||||
handler.add_row(batch, row)
|
||||
self.session.flush()
|
||||
|
||||
with patch.object(mod.BatchMasterView, 'get_batch_handler', return_value=handler):
|
||||
|
||||
view = self.make_view()
|
||||
self.assertRaises(AttributeError, view.get_row_grid_data, batch)
|
||||
|
||||
Session = MagicMock(return_value=self.session)
|
||||
Session.query.side_effect = lambda m: self.session.query(m)
|
||||
with patch.multiple(mod.BatchMasterView, create=True,
|
||||
Session=Session,
|
||||
model_class=MockBatch):
|
||||
|
||||
view = self.make_view()
|
||||
data = view.get_row_grid_data(batch)
|
||||
self.assertIsInstance(data, orm.Query)
|
||||
self.assertEqual(data.count(), 1)
|
||||
|
||||
def test_configure_row_grid(self):
|
||||
handler = MockBatchHandler(self.config)
|
||||
model = self.app.model
|
||||
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
|
||||
batch = handler.make_batch(self.session, created_by=user)
|
||||
self.session.add(batch)
|
||||
row = handler.make_row()
|
||||
handler.add_row(batch, row)
|
||||
self.session.flush()
|
||||
|
||||
with patch.object(mod.BatchMasterView, 'get_batch_handler', return_value=handler):
|
||||
|
||||
Session = MagicMock(return_value=self.session)
|
||||
Session.query.side_effect = lambda m: self.session.query(m)
|
||||
with patch.multiple(mod.BatchMasterView, create=True,
|
||||
Session=Session,
|
||||
model_class=MockBatch):
|
||||
|
||||
with patch.object(self.request, 'matchdict', new={'uuid': batch.uuid}):
|
||||
view = self.make_view()
|
||||
grid = view.make_row_model_grid(batch)
|
||||
self.assertIn('sequence', grid.labels)
|
||||
self.assertEqual(grid.labels['sequence'], "Seq.")
|
||||
|
|
|
@ -334,6 +334,16 @@ class TestMasterView(WebTestCase):
|
|||
model_class=MyModel):
|
||||
self.assertEqual(mod.MasterView.get_config_title(), "Dinosaurs")
|
||||
|
||||
def test_get_row_model_class(self):
|
||||
model = self.app.model
|
||||
|
||||
# no default
|
||||
self.assertIsNone(mod.MasterView.get_row_model_class())
|
||||
|
||||
# class may specify
|
||||
with patch.object(mod.MasterView, 'row_model_class', create=True, new=model.User):
|
||||
self.assertIs(mod.MasterView.get_row_model_class(), model.User)
|
||||
|
||||
##############################
|
||||
# support methods
|
||||
##############################
|
||||
|
@ -1017,6 +1027,53 @@ class TestMasterView(WebTestCase):
|
|||
with patch.object(view, 'get_instance', return_value=setting):
|
||||
response = view.view()
|
||||
|
||||
def test_view_with_rows(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('people', '/people/')
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Whitney Houston")
|
||||
self.session.add(person)
|
||||
user = model.User(username='whitney', person=person)
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
|
||||
get_row_grid_data = MagicMock()
|
||||
with patch.multiple(mod.MasterView, create=True,
|
||||
Session=MagicMock(return_value=self.session),
|
||||
model_class=model.Person,
|
||||
route_prefix='people',
|
||||
has_rows=True,
|
||||
row_model_class=model.User,
|
||||
get_row_grid_data=get_row_grid_data):
|
||||
with patch.object(self.request, 'matchdict', new={'uuid': person.uuid}):
|
||||
view = self.make_view()
|
||||
|
||||
# just for coverage
|
||||
get_row_grid_data.return_value = []
|
||||
response = view.view()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content_type, 'text/html')
|
||||
|
||||
# now with data...
|
||||
get_row_grid_data.return_value = [user]
|
||||
response = view.view()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content_type, 'text/html')
|
||||
|
||||
# then once more as 'partial' - aka. data only
|
||||
with patch.dict(self.request.GET, {'partial': 1}):
|
||||
response = view.view()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
|
||||
# redirects when view is reset
|
||||
with patch.dict(self.request.GET, {'reset-view': '1', 'hash': 'foo'}):
|
||||
# nb. mock current route
|
||||
with patch.object(self.request, 'current_route_url'):
|
||||
response = view.view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_edit(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
|
@ -1501,3 +1558,103 @@ class TestMasterView(WebTestCase):
|
|||
# should now have 0 settings
|
||||
count = self.session.query(model.Setting).count()
|
||||
self.assertEqual(count, 0)
|
||||
|
||||
##############################
|
||||
# row methods
|
||||
##############################
|
||||
|
||||
def test_collect_row_labels(self):
|
||||
|
||||
# default labels
|
||||
view = self.make_view()
|
||||
labels = view.collect_row_labels()
|
||||
self.assertEqual(labels, {})
|
||||
|
||||
# labels come from all classes; subclass wins
|
||||
with patch.object(View, 'row_labels', create=True, new={'foo': "Foo", 'bar': "Bar"}):
|
||||
with patch.object(mod.MasterView, 'row_labels', create=True, new={'foo': "FOO FIGHTERS"}):
|
||||
view = self.make_view()
|
||||
labels = view.collect_row_labels()
|
||||
self.assertEqual(labels, {'foo': "FOO FIGHTERS", 'bar': "Bar"})
|
||||
|
||||
def test_set_row_labels(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Fred Flintstone")
|
||||
self.session.add(person)
|
||||
|
||||
with patch.multiple(mod.MasterView, create=True,
|
||||
model_class=model.Person,
|
||||
has_rows=True,
|
||||
row_model_class=model.User):
|
||||
|
||||
# no labels by default
|
||||
view = self.make_view()
|
||||
grid = view.make_row_model_grid(person, key='person.users', data=[])
|
||||
view.set_row_labels(grid)
|
||||
self.assertEqual(grid.labels, {})
|
||||
|
||||
# labels come from all classes; subclass wins
|
||||
with patch.object(View, 'row_labels', create=True, new={'username': "USERNAME"}):
|
||||
with patch.object(mod.MasterView, 'row_labels', create=True, new={'username': "UserName"}):
|
||||
view = self.make_view()
|
||||
grid = view.make_row_model_grid(person, key='person.users', data=[])
|
||||
view.set_row_labels(grid)
|
||||
self.assertEqual(grid.labels, {'username': "UserName"})
|
||||
|
||||
def test_get_row_grid_data(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Fred Flintstone")
|
||||
self.session.add(person)
|
||||
view = self.make_view()
|
||||
self.assertRaises(NotImplementedError, view.get_row_grid_data, person)
|
||||
|
||||
def test_get_row_grid_columns(self):
|
||||
|
||||
# no default
|
||||
view = self.make_view()
|
||||
self.assertIsNone(view.get_row_grid_columns())
|
||||
|
||||
# class may specify
|
||||
with patch.object(view, 'row_grid_columns', create=True, new=['foo', 'bar']):
|
||||
self.assertEqual(view.get_row_grid_columns(), ['foo', 'bar'])
|
||||
|
||||
def test_get_row_grid_key(self):
|
||||
view = self.make_view()
|
||||
with patch.multiple(mod.MasterView, create=True,
|
||||
model_key='id',
|
||||
grid_key='widgets'):
|
||||
|
||||
self.request.matchdict = {'id': 42}
|
||||
self.assertEqual(view.get_row_grid_key(), 'widgets.42')
|
||||
|
||||
def test_make_row_model_grid(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Barney Rubble")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
|
||||
self.request.matchdict = {'uuid': person.uuid}
|
||||
with patch.multiple(mod.MasterView, create=True,
|
||||
model_class=model.Person):
|
||||
view = self.make_view()
|
||||
|
||||
# specify data
|
||||
grid = view.make_row_model_grid(person, data=[])
|
||||
self.assertIsNone(grid.model_class)
|
||||
self.assertEqual(grid.data, [])
|
||||
|
||||
# fetch data
|
||||
with patch.object(view, 'get_row_grid_data', return_value=[]):
|
||||
grid = view.make_row_model_grid(person)
|
||||
self.assertIsNone(grid.model_class)
|
||||
self.assertEqual(grid.data, [])
|
||||
|
||||
def test_get_rows_title(self):
|
||||
view = self.make_view()
|
||||
|
||||
# no default
|
||||
self.assertIsNone(view.get_rows_title())
|
||||
|
||||
# class may specify
|
||||
with patch.object(view, 'rows_title', create=True, new="Mock Rows"):
|
||||
self.assertEqual(view.get_rows_title(), "Mock Rows")
|
||||
|
|
Loading…
Reference in a new issue