feat: add basic autocomplete support, for Person
URL endpoint only for now, form widget to come later
This commit is contained in:
parent
4bf2bb42fb
commit
9d261de45a
6 changed files with 391 additions and 203 deletions
|
@ -263,6 +263,12 @@ class MasterView(View):
|
||||||
|
|
||||||
This is optional; see also :meth:`get_form_fields()`.
|
This is optional; see also :meth:`get_form_fields()`.
|
||||||
|
|
||||||
|
.. attribute:: has_autocomplete
|
||||||
|
|
||||||
|
Boolean indicating whether the view model supports
|
||||||
|
"autocomplete" - i.e. it should have an :meth:`autocomplete()`
|
||||||
|
view. Default is ``False``.
|
||||||
|
|
||||||
.. attribute:: configurable
|
.. attribute:: configurable
|
||||||
|
|
||||||
Boolean indicating whether the master view supports
|
Boolean indicating whether the master view supports
|
||||||
|
@ -286,6 +292,7 @@ class MasterView(View):
|
||||||
viewable = True
|
viewable = True
|
||||||
editable = True
|
editable = True
|
||||||
deletable = True
|
deletable = True
|
||||||
|
has_autocomplete = False
|
||||||
configurable = False
|
configurable = False
|
||||||
|
|
||||||
# current action
|
# current action
|
||||||
|
@ -573,6 +580,84 @@ class MasterView(View):
|
||||||
session = self.app.get_session(obj)
|
session = self.app.get_session(obj)
|
||||||
session.delete(obj)
|
session.delete(obj)
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# autocomplete methods
|
||||||
|
##############################
|
||||||
|
|
||||||
|
def autocomplete(self):
|
||||||
|
"""
|
||||||
|
View which accepts a single ``term`` param, and returns a JSON
|
||||||
|
list of autocomplete results to match.
|
||||||
|
|
||||||
|
By default, this view is included only if
|
||||||
|
:attr:`has_autocomplete` is true. It usually maps to a URL
|
||||||
|
like ``/widgets/autocomplete``.
|
||||||
|
|
||||||
|
Subclass generally does not need to override this method, but
|
||||||
|
rather should override the others which this calls:
|
||||||
|
|
||||||
|
* :meth:`autocomplete_data()`
|
||||||
|
* :meth:`autocomplete_normalize()`
|
||||||
|
"""
|
||||||
|
term = self.request.GET.get('term', '')
|
||||||
|
if not term:
|
||||||
|
return []
|
||||||
|
|
||||||
|
data = self.autocomplete_data(term)
|
||||||
|
if not data:
|
||||||
|
return []
|
||||||
|
|
||||||
|
max_results = 100 # TODO
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for obj in data[:max_results]:
|
||||||
|
normal = self.autocomplete_normalize(obj)
|
||||||
|
if normal:
|
||||||
|
results.append(normal)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def autocomplete_data(self, term):
|
||||||
|
"""
|
||||||
|
Should return the data/query for the "matching" model records,
|
||||||
|
based on autocomplete search term. This is called by
|
||||||
|
:meth:`autocomplete()`.
|
||||||
|
|
||||||
|
Subclass must override this; default logic returns no data.
|
||||||
|
|
||||||
|
:param term: String search term as-is from user, e.g. "foo bar".
|
||||||
|
|
||||||
|
:returns: List of data records, or SQLAlchemy query.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def autocomplete_normalize(self, obj):
|
||||||
|
"""
|
||||||
|
Should return a "normalized" version of the given model
|
||||||
|
record, suitable for autocomplete JSON results. This is
|
||||||
|
called by :meth:`autocomplete()`.
|
||||||
|
|
||||||
|
Subclass may need to override this; default logic is
|
||||||
|
simplistic but will work for basic models. It returns the
|
||||||
|
"autocomplete results" dict for the object::
|
||||||
|
|
||||||
|
{
|
||||||
|
'value': obj.uuid,
|
||||||
|
'label': str(obj),
|
||||||
|
}
|
||||||
|
|
||||||
|
The 2 keys shown are required; any other keys will be ignored
|
||||||
|
by the view logic but may be useful on the frontend widget.
|
||||||
|
|
||||||
|
:param obj: Model record/instance.
|
||||||
|
|
||||||
|
:returns: Dict of "autocomplete results" format, as shown
|
||||||
|
above.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'value': obj.uuid,
|
||||||
|
'label': str(obj),
|
||||||
|
}
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# configure methods
|
# configure methods
|
||||||
##############################
|
##############################
|
||||||
|
@ -1888,6 +1973,15 @@ class MasterView(View):
|
||||||
f'{permission_prefix}.delete',
|
f'{permission_prefix}.delete',
|
||||||
f"Delete {model_title}")
|
f"Delete {model_title}")
|
||||||
|
|
||||||
|
# autocomplete
|
||||||
|
if cls.has_autocomplete:
|
||||||
|
config.add_route(f'{route_prefix}.autocomplete',
|
||||||
|
f'{url_prefix}/autocomplete')
|
||||||
|
config.add_view(cls, attr='autocomplete',
|
||||||
|
route_name=f'{route_prefix}.autocomplete',
|
||||||
|
renderer='json',
|
||||||
|
permission=f'{route_prefix}.list')
|
||||||
|
|
||||||
# configure
|
# configure
|
||||||
if cls.configurable:
|
if cls.configurable:
|
||||||
config.add_route(f'{route_prefix}.configure',
|
config.add_route(f'{route_prefix}.configure',
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
Views for people
|
Views for people
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from wuttjamaican.db.model import Person
|
from wuttjamaican.db.model import Person
|
||||||
from wuttaweb.views import MasterView
|
from wuttaweb.views import MasterView
|
||||||
|
|
||||||
|
@ -46,6 +48,7 @@ class PersonView(MasterView):
|
||||||
model_title_plural = "People"
|
model_title_plural = "People"
|
||||||
route_prefix = 'people'
|
route_prefix = 'people'
|
||||||
sort_defaults = 'full_name'
|
sort_defaults = 'full_name'
|
||||||
|
has_autocomplete = True
|
||||||
|
|
||||||
grid_columns = [
|
grid_columns = [
|
||||||
'full_name',
|
'full_name',
|
||||||
|
@ -85,6 +88,17 @@ class PersonView(MasterView):
|
||||||
if 'users' in f:
|
if 'users' in f:
|
||||||
f.fields.remove('users')
|
f.fields.remove('users')
|
||||||
|
|
||||||
|
def autocomplete_query(self, term):
|
||||||
|
""" """
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
query = session.query(model.Person)
|
||||||
|
criteria = [model.Person.full_name.ilike(f'%{word}%')
|
||||||
|
for word in term.split()]
|
||||||
|
query = query.filter(sa.and_(*criteria))\
|
||||||
|
.order_by(model.Person.full_name)
|
||||||
|
return query
|
||||||
|
|
||||||
def view_profile(self, session=None):
|
def view_profile(self, session=None):
|
||||||
""" """
|
""" """
|
||||||
person = self.get_instance(session=session)
|
person = self.get_instance(session=session)
|
||||||
|
|
|
@ -197,6 +197,14 @@ class SettingView(MasterView):
|
||||||
model_title = "Raw Setting"
|
model_title = "Raw Setting"
|
||||||
sort_defaults = 'name'
|
sort_defaults = 'name'
|
||||||
|
|
||||||
|
# TODO: master should handle this (per model key)
|
||||||
|
def configure_grid(self, g):
|
||||||
|
""" """
|
||||||
|
super().configure_grid(g)
|
||||||
|
|
||||||
|
# name
|
||||||
|
g.set_link('name')
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
""" """
|
""" """
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from pyramid.response import Response
|
||||||
from pyramid.httpexceptions import HTTPNotFound
|
from pyramid.httpexceptions import HTTPNotFound
|
||||||
|
|
||||||
from wuttjamaican.conf import WuttaConfig
|
from wuttjamaican.conf import WuttaConfig
|
||||||
from wuttaweb.views import master
|
from wuttaweb.views import master as mod
|
||||||
from wuttaweb.views import View
|
from wuttaweb.views import View
|
||||||
from wuttaweb.subscribers import new_request_set_user
|
from wuttaweb.subscribers import new_request_set_user
|
||||||
from tests.util import WebTestCase
|
from tests.util import WebTestCase
|
||||||
|
@ -19,14 +19,15 @@ from tests.util import WebTestCase
|
||||||
class TestMasterView(WebTestCase):
|
class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
def make_view(self):
|
def make_view(self):
|
||||||
return master.MasterView(self.request)
|
return mod.MasterView(self.request)
|
||||||
|
|
||||||
def test_defaults(self):
|
def test_defaults(self):
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Widget',
|
model_name='Widget',
|
||||||
model_key='uuid',
|
model_key='uuid',
|
||||||
|
has_autocomplete=True,
|
||||||
configurable=True):
|
configurable=True):
|
||||||
master.MasterView.defaults(self.pyramid_config)
|
mod.MasterView.defaults(self.pyramid_config)
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# class methods
|
# class methods
|
||||||
|
@ -35,317 +36,315 @@ class TestMasterView(WebTestCase):
|
||||||
def test_get_model_class(self):
|
def test_get_model_class(self):
|
||||||
|
|
||||||
# no model class by default
|
# no model class by default
|
||||||
self.assertIsNone(master.MasterView.get_model_class())
|
self.assertIsNone(mod.MasterView.get_model_class())
|
||||||
|
|
||||||
# subclass may specify
|
# subclass may specify
|
||||||
MyModel = MagicMock()
|
MyModel = MagicMock()
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertIs(master.MasterView.get_model_class(), MyModel)
|
self.assertIs(mod.MasterView.get_model_class(), MyModel)
|
||||||
|
|
||||||
def test_get_model_name(self):
|
def test_get_model_name(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_model_name)
|
self.assertRaises(AttributeError, mod.MasterView.get_model_name)
|
||||||
|
|
||||||
# subclass may specify model name
|
# subclass may specify model name
|
||||||
master.MasterView.model_name = 'Widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_name(), 'Widget')
|
model_name='Widget'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_model_name(), 'Widget')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Blaster')
|
MyModel = MagicMock(__name__='Blaster')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_model_name(), 'Blaster')
|
self.assertEqual(mod.MasterView.get_model_name(), 'Blaster')
|
||||||
|
|
||||||
def test_get_model_name_normalized(self):
|
def test_get_model_name_normalized(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_model_name_normalized)
|
self.assertRaises(AttributeError, mod.MasterView.get_model_name_normalized)
|
||||||
|
|
||||||
# subclass may specify *normalized* model name
|
# subclass may specify *normalized* model name
|
||||||
master.MasterView.model_name_normalized = 'widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_name_normalized(), 'widget')
|
model_name_normalized='widget'):
|
||||||
del master.MasterView.model_name_normalized
|
self.assertEqual(mod.MasterView.get_model_name_normalized(), 'widget')
|
||||||
|
|
||||||
# or it may specify *standard* model name
|
# or it may specify *standard* model name
|
||||||
master.MasterView.model_name = 'Blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_name_normalized(), 'blaster')
|
model_name='Blaster'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_model_name_normalized(), 'blaster')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Dinosaur')
|
MyModel = MagicMock(__name__='Dinosaur')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_model_name_normalized(), 'dinosaur')
|
self.assertEqual(mod.MasterView.get_model_name_normalized(), 'dinosaur')
|
||||||
|
|
||||||
def test_get_model_title(self):
|
def test_get_model_title(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_model_title)
|
self.assertRaises(AttributeError, mod.MasterView.get_model_title)
|
||||||
|
|
||||||
# subclass may specify model title
|
# subclass may specify model title
|
||||||
master.MasterView.model_title = 'Wutta Widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_title(), "Wutta Widget")
|
model_title='Wutta Widget'):
|
||||||
del master.MasterView.model_title
|
self.assertEqual(mod.MasterView.get_model_title(), "Wutta Widget")
|
||||||
|
|
||||||
# or it may specify model name
|
# or it may specify model name
|
||||||
master.MasterView.model_name = 'Blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_title(), "Blaster")
|
model_name='Blaster'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_model_title(), "Blaster")
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Dinosaur')
|
MyModel = MagicMock(__name__='Dinosaur')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_model_title(), "Dinosaur")
|
self.assertEqual(mod.MasterView.get_model_title(), "Dinosaur")
|
||||||
|
|
||||||
def test_get_model_title_plural(self):
|
def test_get_model_title_plural(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_model_title_plural)
|
self.assertRaises(AttributeError, mod.MasterView.get_model_title_plural)
|
||||||
|
|
||||||
# subclass may specify *plural* model title
|
# subclass may specify *plural* model title
|
||||||
master.MasterView.model_title_plural = 'People'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_title_plural(), "People")
|
model_title_plural='People'):
|
||||||
del master.MasterView.model_title_plural
|
self.assertEqual(mod.MasterView.get_model_title_plural(), "People")
|
||||||
|
|
||||||
# or it may specify *singular* model title
|
# or it may specify *singular* model title
|
||||||
master.MasterView.model_title = 'Wutta Widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_title_plural(), "Wutta Widgets")
|
model_title='Wutta Widget'):
|
||||||
del master.MasterView.model_title
|
self.assertEqual(mod.MasterView.get_model_title_plural(), "Wutta Widgets")
|
||||||
|
|
||||||
# or it may specify model name
|
# or it may specify model name
|
||||||
master.MasterView.model_name = 'Blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_title_plural(), "Blasters")
|
model_name='Blaster'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_model_title_plural(), "Blasters")
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Dinosaur')
|
MyModel = MagicMock(__name__='Dinosaur')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_model_title_plural(), "Dinosaurs")
|
self.assertEqual(mod.MasterView.get_model_title_plural(), "Dinosaurs")
|
||||||
|
|
||||||
def test_get_model_key(self):
|
def test_get_model_key(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_model_key)
|
self.assertRaises(AttributeError, mod.MasterView.get_model_key)
|
||||||
|
|
||||||
# subclass may specify model key
|
# subclass may specify model key
|
||||||
master.MasterView.model_key = 'uuid'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_model_key(), ('uuid',))
|
model_key='uuid'):
|
||||||
del master.MasterView.model_key
|
self.assertEqual(mod.MasterView.get_model_key(), ('uuid',))
|
||||||
|
|
||||||
def test_get_route_prefix(self):
|
def test_get_route_prefix(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_route_prefix)
|
self.assertRaises(AttributeError, mod.MasterView.get_route_prefix)
|
||||||
|
|
||||||
# subclass may specify route prefix
|
# subclass may specify route prefix
|
||||||
master.MasterView.route_prefix = 'widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_route_prefix(), 'widgets')
|
route_prefix='widgets'):
|
||||||
del master.MasterView.route_prefix
|
self.assertEqual(mod.MasterView.get_route_prefix(), 'widgets')
|
||||||
|
|
||||||
# subclass may specify *normalized* model name
|
# subclass may specify *normalized* model name
|
||||||
master.MasterView.model_name_normalized = 'blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_route_prefix(), 'blasters')
|
model_name_normalized='blaster'):
|
||||||
del master.MasterView.model_name_normalized
|
self.assertEqual(mod.MasterView.get_route_prefix(), 'blasters')
|
||||||
|
|
||||||
# or it may specify *standard* model name
|
# or it may specify *standard* model name
|
||||||
master.MasterView.model_name = 'Dinosaur'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_route_prefix(), 'dinosaurs')
|
model_name = 'Dinosaur'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_route_prefix(), 'dinosaurs')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Truck')
|
MyModel = MagicMock(__name__='Truck')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_route_prefix(), 'trucks')
|
self.assertEqual(mod.MasterView.get_route_prefix(), 'trucks')
|
||||||
|
|
||||||
def test_get_permission_prefix(self):
|
def test_get_permission_prefix(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_permission_prefix)
|
self.assertRaises(AttributeError, mod.MasterView.get_permission_prefix)
|
||||||
|
|
||||||
# subclass may specify permission prefix
|
# subclass may specify permission prefix
|
||||||
with patch.object(master.MasterView, 'permission_prefix', new='widgets', create=True):
|
with patch.object(mod.MasterView, 'permission_prefix', new='widgets', create=True):
|
||||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'widgets')
|
self.assertEqual(mod.MasterView.get_permission_prefix(), 'widgets')
|
||||||
|
|
||||||
# subclass may specify route prefix
|
# subclass may specify route prefix
|
||||||
with patch.object(master.MasterView, 'route_prefix', new='widgets', create=True):
|
with patch.object(mod.MasterView, 'route_prefix', new='widgets', create=True):
|
||||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'widgets')
|
self.assertEqual(mod.MasterView.get_permission_prefix(), 'widgets')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
Truck = MagicMock(__name__='Truck')
|
Truck = MagicMock(__name__='Truck')
|
||||||
with patch.object(master.MasterView, 'model_class', new=Truck, create=True):
|
with patch.object(mod.MasterView, 'model_class', new=Truck, create=True):
|
||||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'trucks')
|
self.assertEqual(mod.MasterView.get_permission_prefix(), 'trucks')
|
||||||
|
|
||||||
def test_get_url_prefix(self):
|
def test_get_url_prefix(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_url_prefix)
|
self.assertRaises(AttributeError, mod.MasterView.get_url_prefix)
|
||||||
|
|
||||||
# subclass may specify url prefix
|
# subclass may specify url prefix
|
||||||
master.MasterView.url_prefix = '/widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_url_prefix(), '/widgets')
|
url_prefix='/widgets'):
|
||||||
del master.MasterView.url_prefix
|
self.assertEqual(mod.MasterView.get_url_prefix(), '/widgets')
|
||||||
|
|
||||||
# or it may specify route prefix
|
# or it may specify route prefix
|
||||||
master.MasterView.route_prefix = 'trucks'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_url_prefix(), '/trucks')
|
route_prefix='trucks'):
|
||||||
del master.MasterView.route_prefix
|
self.assertEqual(mod.MasterView.get_url_prefix(), '/trucks')
|
||||||
|
|
||||||
# or it may specify *normalized* model name
|
# or it may specify *normalized* model name
|
||||||
master.MasterView.model_name_normalized = 'blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_url_prefix(), '/blasters')
|
model_name_normalized='blaster'):
|
||||||
del master.MasterView.model_name_normalized
|
self.assertEqual(mod.MasterView.get_url_prefix(), '/blasters')
|
||||||
|
|
||||||
# or it may specify *standard* model name
|
# or it may specify *standard* model name
|
||||||
master.MasterView.model_name = 'Dinosaur'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_url_prefix(), '/dinosaurs')
|
model_name='Dinosaur'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_url_prefix(), '/dinosaurs')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Machine')
|
MyModel = MagicMock(__name__='Machine')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_url_prefix(), '/machines')
|
self.assertEqual(mod.MasterView.get_url_prefix(), '/machines')
|
||||||
|
|
||||||
def test_get_instance_url_prefix(self):
|
def test_get_instance_url_prefix(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_instance_url_prefix)
|
self.assertRaises(AttributeError, mod.MasterView.get_instance_url_prefix)
|
||||||
|
|
||||||
# typical example with url_prefix and simple key
|
# typical example with url_prefix and simple key
|
||||||
master.MasterView.url_prefix = '/widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
master.MasterView.model_key = 'uuid'
|
url_prefix='/widgets',
|
||||||
self.assertEqual(master.MasterView.get_instance_url_prefix(), '/widgets/{uuid}')
|
model_key='uuid'):
|
||||||
del master.MasterView.url_prefix
|
self.assertEqual(mod.MasterView.get_instance_url_prefix(), '/widgets/{uuid}')
|
||||||
del master.MasterView.model_key
|
|
||||||
|
|
||||||
# typical example with composite key
|
# typical example with composite key
|
||||||
master.MasterView.url_prefix = '/widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
master.MasterView.model_key = ('foo', 'bar')
|
url_prefix='/widgets',
|
||||||
self.assertEqual(master.MasterView.get_instance_url_prefix(), '/widgets/{foo}|{bar}')
|
model_key=('foo', 'bar')):
|
||||||
del master.MasterView.url_prefix
|
self.assertEqual(mod.MasterView.get_instance_url_prefix(), '/widgets/{foo}|{bar}')
|
||||||
del master.MasterView.model_key
|
|
||||||
|
|
||||||
def test_get_template_prefix(self):
|
def test_get_template_prefix(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_template_prefix)
|
self.assertRaises(AttributeError, mod.MasterView.get_template_prefix)
|
||||||
|
|
||||||
# subclass may specify template prefix
|
# subclass may specify template prefix
|
||||||
master.MasterView.template_prefix = '/widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/widgets')
|
template_prefix='/widgets'):
|
||||||
del master.MasterView.template_prefix
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/widgets')
|
||||||
|
|
||||||
# or it may specify url prefix
|
# or it may specify url prefix
|
||||||
master.MasterView.url_prefix = '/trees'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/trees')
|
url_prefix='/trees'):
|
||||||
del master.MasterView.url_prefix
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/trees')
|
||||||
|
|
||||||
# or it may specify route prefix
|
# or it may specify route prefix
|
||||||
master.MasterView.route_prefix = 'trucks'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/trucks')
|
route_prefix='trucks'):
|
||||||
del master.MasterView.route_prefix
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/trucks')
|
||||||
|
|
||||||
# or it may specify *normalized* model name
|
# or it may specify *normalized* model name
|
||||||
master.MasterView.model_name_normalized = 'blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/blasters')
|
model_name_normalized='blaster'):
|
||||||
del master.MasterView.model_name_normalized
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/blasters')
|
||||||
|
|
||||||
# or it may specify *standard* model name
|
# or it may specify *standard* model name
|
||||||
master.MasterView.model_name = 'Dinosaur'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/dinosaurs')
|
model_name='Dinosaur'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/dinosaurs')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Machine')
|
MyModel = MagicMock(__name__='Machine')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_template_prefix(), '/machines')
|
self.assertEqual(mod.MasterView.get_template_prefix(), '/machines')
|
||||||
|
|
||||||
def test_get_grid_key(self):
|
def test_get_grid_key(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_grid_key)
|
self.assertRaises(AttributeError, mod.MasterView.get_grid_key)
|
||||||
|
|
||||||
# subclass may specify grid key
|
# subclass may specify grid key
|
||||||
master.MasterView.grid_key = 'widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_grid_key(), 'widgets')
|
grid_key='widgets'):
|
||||||
del master.MasterView.grid_key
|
self.assertEqual(mod.MasterView.get_grid_key(), 'widgets')
|
||||||
|
|
||||||
# or it may specify route prefix
|
# or it may specify route prefix
|
||||||
master.MasterView.route_prefix = 'trucks'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_grid_key(), 'trucks')
|
route_prefix='trucks'):
|
||||||
del master.MasterView.route_prefix
|
self.assertEqual(mod.MasterView.get_grid_key(), 'trucks')
|
||||||
|
|
||||||
# or it may specify *normalized* model name
|
# or it may specify *normalized* model name
|
||||||
master.MasterView.model_name_normalized = 'blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_grid_key(), 'blasters')
|
model_name_normalized='blaster'):
|
||||||
del master.MasterView.model_name_normalized
|
self.assertEqual(mod.MasterView.get_grid_key(), 'blasters')
|
||||||
|
|
||||||
# or it may specify *standard* model name
|
# or it may specify *standard* model name
|
||||||
master.MasterView.model_name = 'Dinosaur'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_grid_key(), 'dinosaurs')
|
model_name='Dinosaur'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_grid_key(), 'dinosaurs')
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Machine')
|
MyModel = MagicMock(__name__='Machine')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_grid_key(), 'machines')
|
self.assertEqual(mod.MasterView.get_grid_key(), 'machines')
|
||||||
|
|
||||||
def test_get_config_title(self):
|
def test_get_config_title(self):
|
||||||
|
|
||||||
# error by default (since no model class)
|
# error by default (since no model class)
|
||||||
self.assertRaises(AttributeError, master.MasterView.get_config_title)
|
self.assertRaises(AttributeError, mod.MasterView.get_config_title)
|
||||||
|
|
||||||
# subclass may specify config title
|
# subclass may specify config title
|
||||||
master.MasterView.config_title = 'Widgets'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_config_title(), "Widgets")
|
config_title='Widgets'):
|
||||||
del master.MasterView.config_title
|
self.assertEqual(mod.MasterView.get_config_title(), "Widgets")
|
||||||
|
|
||||||
# subclass may specify *plural* model title
|
# subclass may specify *plural* model title
|
||||||
master.MasterView.model_title_plural = 'People'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_config_title(), "People")
|
model_title_plural='People'):
|
||||||
del master.MasterView.model_title_plural
|
self.assertEqual(mod.MasterView.get_config_title(), "People")
|
||||||
|
|
||||||
# or it may specify *singular* model title
|
# or it may specify *singular* model title
|
||||||
master.MasterView.model_title = 'Wutta Widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_config_title(), "Wutta Widgets")
|
model_title='Wutta Widget'):
|
||||||
del master.MasterView.model_title
|
self.assertEqual(mod.MasterView.get_config_title(), "Wutta Widgets")
|
||||||
|
|
||||||
# or it may specify model name
|
# or it may specify model name
|
||||||
master.MasterView.model_name = 'Blaster'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertEqual(master.MasterView.get_config_title(), "Blasters")
|
model_name='Blaster'):
|
||||||
del master.MasterView.model_name
|
self.assertEqual(mod.MasterView.get_config_title(), "Blasters")
|
||||||
|
|
||||||
# or it may specify model class
|
# or it may specify model class
|
||||||
MyModel = MagicMock(__name__='Dinosaur')
|
MyModel = MagicMock(__name__='Dinosaur')
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=MyModel):
|
model_class=MyModel):
|
||||||
self.assertEqual(master.MasterView.get_config_title(), "Dinosaurs")
|
self.assertEqual(mod.MasterView.get_config_title(), "Dinosaurs")
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# support methods
|
# support methods
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
def test_get_class_hierarchy(self):
|
def test_get_class_hierarchy(self):
|
||||||
class MyView(master.MasterView):
|
class MyView(mod.MasterView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
view = MyView(self.request)
|
view = MyView(self.request)
|
||||||
classes = view.get_class_hierarchy()
|
classes = view.get_class_hierarchy()
|
||||||
self.assertEqual(classes, [View, master.MasterView, MyView])
|
self.assertEqual(classes, [View, mod.MasterView, MyView])
|
||||||
|
|
||||||
def test_has_perm(self):
|
def test_has_perm(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
auth = self.app.get_auth_handler()
|
auth = self.app.get_auth_handler()
|
||||||
|
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting'):
|
model_name='Setting'):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
|
||||||
|
@ -374,7 +373,7 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
auth = self.app.get_auth_handler()
|
auth = self.app.get_auth_handler()
|
||||||
|
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting'):
|
model_name='Setting'):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
|
||||||
|
@ -410,20 +409,20 @@ class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
# basic sanity check using /master/index.mako
|
# basic sanity check using /master/index.mako
|
||||||
# (nb. it skips /widgets/index.mako since that doesn't exist)
|
# (nb. it skips /widgets/index.mako since that doesn't exist)
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Widget',
|
model_name='Widget',
|
||||||
creatable=False):
|
creatable=False):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
response = view.render_to_response('index', {})
|
response = view.render_to_response('index', {})
|
||||||
self.assertIsInstance(response, Response)
|
self.assertIsInstance(response, Response)
|
||||||
|
|
||||||
# basic sanity check using /appinfo/index.mako
|
# basic sanity check using /appinfo/index.mako
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='AppInfo',
|
model_name='AppInfo',
|
||||||
route_prefix='appinfo',
|
route_prefix='appinfo',
|
||||||
url_prefix='/appinfo',
|
url_prefix='/appinfo',
|
||||||
creatable=False):
|
creatable=False):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
response = view.render_to_response('index', {
|
response = view.render_to_response('index', {
|
||||||
# nb. grid is required for this template
|
# nb. grid is required for this template
|
||||||
'grid': MagicMock(),
|
'grid': MagicMock(),
|
||||||
|
@ -431,15 +430,15 @@ class TestMasterView(WebTestCase):
|
||||||
self.assertIsInstance(response, Response)
|
self.assertIsInstance(response, Response)
|
||||||
|
|
||||||
# bad template name causes error
|
# bad template name causes error
|
||||||
master.MasterView.model_name = 'Widget'
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
self.assertRaises(IOError, view.render_to_response, 'nonexistent', {})
|
model_name='Widget'):
|
||||||
del master.MasterView.model_name
|
self.assertRaises(IOError, view.render_to_response, 'nonexistent', {})
|
||||||
|
|
||||||
def test_get_index_title(self):
|
def test_get_index_title(self):
|
||||||
master.MasterView.model_title_plural = "Wutta Widgets"
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
view = master.MasterView(self.request)
|
model_title_plural = "Wutta Widgets"):
|
||||||
self.assertEqual(view.get_index_title(), "Wutta Widgets")
|
view = mod.MasterView(self.request)
|
||||||
del master.MasterView.model_title_plural
|
self.assertEqual(view.get_index_title(), "Wutta Widgets")
|
||||||
|
|
||||||
def test_collect_labels(self):
|
def test_collect_labels(self):
|
||||||
|
|
||||||
|
@ -450,14 +449,14 @@ class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
# labels come from all classes; subclass wins
|
# labels come from all classes; subclass wins
|
||||||
with patch.object(View, 'labels', new={'foo': "Foo", 'bar': "Bar"}, create=True):
|
with patch.object(View, 'labels', new={'foo': "Foo", 'bar': "Bar"}, create=True):
|
||||||
with patch.object(master.MasterView, 'labels', new={'foo': "FOO FIGHTERS"}, create=True):
|
with patch.object(mod.MasterView, 'labels', new={'foo': "FOO FIGHTERS"}, create=True):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
labels = view.collect_labels()
|
labels = view.collect_labels()
|
||||||
self.assertEqual(labels, {'foo': "FOO FIGHTERS", 'bar': "Bar"})
|
self.assertEqual(labels, {'foo': "FOO FIGHTERS", 'bar': "Bar"})
|
||||||
|
|
||||||
def test_set_labels(self):
|
def test_set_labels(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
with patch.object(master.MasterView, 'model_class', new=model.Setting, create=True):
|
with patch.object(mod.MasterView, 'model_class', new=model.Setting, create=True):
|
||||||
|
|
||||||
# no labels by default
|
# no labels by default
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
@ -466,7 +465,7 @@ class TestMasterView(WebTestCase):
|
||||||
self.assertEqual(grid.labels, {})
|
self.assertEqual(grid.labels, {})
|
||||||
|
|
||||||
# labels come from all classes; subclass wins
|
# labels come from all classes; subclass wins
|
||||||
with patch.object(master.MasterView, 'labels', new={'name': "SETTING NAME"}, create=True):
|
with patch.object(mod.MasterView, 'labels', new={'name': "SETTING NAME"}, create=True):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
view.set_labels(grid)
|
view.set_labels(grid)
|
||||||
self.assertEqual(grid.labels, {'name': "SETTING NAME"})
|
self.assertEqual(grid.labels, {'name': "SETTING NAME"})
|
||||||
|
@ -475,27 +474,27 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
# no model class
|
# no model class
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Widget',
|
model_name='Widget',
|
||||||
model_key='uuid'):
|
model_key='uuid'):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
grid = view.make_model_grid()
|
grid = view.make_model_grid()
|
||||||
self.assertIsNone(grid.model_class)
|
self.assertIsNone(grid.model_class)
|
||||||
|
|
||||||
# explicit model class
|
# explicit model class
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
grid = view.make_model_grid(session=self.session)
|
grid = view.make_model_grid(session=self.session)
|
||||||
self.assertIs(grid.model_class, model.Setting)
|
self.assertIs(grid.model_class, model.Setting)
|
||||||
|
|
||||||
# no actions by default
|
# no actions by default
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
grid = view.make_model_grid(session=self.session)
|
grid = view.make_model_grid(session=self.session)
|
||||||
self.assertEqual(grid.actions, [])
|
self.assertEqual(grid.actions, [])
|
||||||
|
|
||||||
# now let's test some more actions logic
|
# now let's test some more actions logic
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting,
|
model_class=model.Setting,
|
||||||
viewable=True,
|
viewable=True,
|
||||||
editable=True,
|
editable=True,
|
||||||
|
@ -518,14 +517,14 @@ class TestMasterView(WebTestCase):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
|
||||||
# empty by default
|
# empty by default
|
||||||
self.assertFalse(hasattr(master.MasterView, 'model_class'))
|
self.assertFalse(hasattr(mod.MasterView, 'model_class'))
|
||||||
data = view.get_grid_data(session=self.session)
|
data = view.get_grid_data(session=self.session)
|
||||||
self.assertEqual(data, [])
|
self.assertEqual(data, [])
|
||||||
|
|
||||||
# grid with model class will produce data query
|
# grid with model class will produce data query
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
query = view.get_grid_data(session=self.session)
|
query = view.get_grid_data(session=self.session)
|
||||||
self.assertIsInstance(query, orm.Query)
|
self.assertIsInstance(query, orm.Query)
|
||||||
data = query.all()
|
data = query.all()
|
||||||
|
@ -536,9 +535,9 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
# uuid field is pruned
|
# uuid field is pruned
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
grid = view.make_grid(model_class=model.Setting,
|
grid = view.make_grid(model_class=model.Setting,
|
||||||
columns=['uuid', 'name', 'value'])
|
columns=['uuid', 'name', 'value'])
|
||||||
self.assertIn('uuid', grid.columns)
|
self.assertIn('uuid', grid.columns)
|
||||||
|
@ -574,13 +573,13 @@ class TestMasterView(WebTestCase):
|
||||||
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
||||||
|
|
||||||
# default not implemented
|
# default not implemented
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
self.assertRaises(NotImplementedError, view.get_instance)
|
self.assertRaises(NotImplementedError, view.get_instance)
|
||||||
|
|
||||||
# fetch from DB if model class is known
|
# fetch from DB if model class is known
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
|
|
||||||
# existing setting is returned
|
# existing setting is returned
|
||||||
self.request.matchdict = {'name': 'foo'}
|
self.request.matchdict = {'name': 'foo'}
|
||||||
|
@ -599,9 +598,9 @@ class TestMasterView(WebTestCase):
|
||||||
self.session.add(setting)
|
self.session.add(setting)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
master.MasterView.defaults(self.pyramid_config)
|
mod.MasterView.defaults(self.pyramid_config)
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
url = view.get_action_url_view(setting, 0)
|
url = view.get_action_url_view(setting, 0)
|
||||||
self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
|
self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
|
||||||
|
@ -611,9 +610,9 @@ class TestMasterView(WebTestCase):
|
||||||
setting = model.Setting(name='foo', value='bar')
|
setting = model.Setting(name='foo', value='bar')
|
||||||
self.session.add(setting)
|
self.session.add(setting)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
master.MasterView.defaults(self.pyramid_config)
|
mod.MasterView.defaults(self.pyramid_config)
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
|
||||||
# typical
|
# typical
|
||||||
|
@ -630,9 +629,9 @@ class TestMasterView(WebTestCase):
|
||||||
setting = model.Setting(name='foo', value='bar')
|
setting = model.Setting(name='foo', value='bar')
|
||||||
self.session.add(setting)
|
self.session.add(setting)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
master.MasterView.defaults(self.pyramid_config)
|
mod.MasterView.defaults(self.pyramid_config)
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
|
||||||
# typical
|
# typical
|
||||||
|
@ -648,15 +647,15 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
# no model class
|
# no model class
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Widget',
|
model_name='Widget',
|
||||||
model_key='uuid'):
|
model_key='uuid'):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
form = view.make_model_form()
|
form = view.make_model_form()
|
||||||
self.assertIsNone(form.model_class)
|
self.assertIsNone(form.model_class)
|
||||||
|
|
||||||
# explicit model class
|
# explicit model class
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
form = view.make_model_form()
|
form = view.make_model_form()
|
||||||
self.assertIs(form.model_class, model.Setting)
|
self.assertIs(form.model_class, model.Setting)
|
||||||
|
@ -665,9 +664,9 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
# uuid field is pruned
|
# uuid field is pruned
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
form = view.make_form(model_class=model.Setting,
|
form = view.make_form(model_class=model.Setting,
|
||||||
fields=['uuid', 'name', 'value'])
|
fields=['uuid', 'name', 'value'])
|
||||||
self.assertIn('uuid', form.fields)
|
self.assertIn('uuid', form.fields)
|
||||||
|
@ -681,17 +680,17 @@ class TestMasterView(WebTestCase):
|
||||||
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
||||||
|
|
||||||
# no model class
|
# no model class
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Widget',
|
model_name='Widget',
|
||||||
model_key='uuid'):
|
model_key='uuid'):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
form = view.make_model_form(fields=['name', 'description'])
|
form = view.make_model_form(fields=['name', 'description'])
|
||||||
form.validated = {'name': 'first'}
|
form.validated = {'name': 'first'}
|
||||||
obj = view.objectify(form)
|
obj = view.objectify(form)
|
||||||
self.assertIs(obj, form.validated)
|
self.assertIs(obj, form.validated)
|
||||||
|
|
||||||
# explicit model class (editing)
|
# explicit model class (editing)
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting,
|
model_class=model.Setting,
|
||||||
editing=True):
|
editing=True):
|
||||||
form = view.make_model_form()
|
form = view.make_model_form()
|
||||||
|
@ -703,7 +702,7 @@ class TestMasterView(WebTestCase):
|
||||||
self.assertEqual(obj.value, 'blarg')
|
self.assertEqual(obj.value, 'blarg')
|
||||||
|
|
||||||
# explicit model class (creating)
|
# explicit model class (creating)
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting,
|
model_class=model.Setting,
|
||||||
creating=True):
|
creating=True):
|
||||||
form = view.make_model_form()
|
form = view.make_model_form()
|
||||||
|
@ -715,9 +714,9 @@ class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
def test_persist(self):
|
def test_persist(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting):
|
model_class=model.Setting):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
|
|
||||||
# new instance is persisted
|
# new instance is persisted
|
||||||
setting = model.Setting(name='foo', value='bar')
|
setting = model.Setting(name='foo', value='bar')
|
||||||
|
@ -741,12 +740,12 @@ class TestMasterView(WebTestCase):
|
||||||
self.pyramid_config.add_route('settings.delete', '/settings/{name}/delete')
|
self.pyramid_config.add_route('settings.delete', '/settings/{name}/delete')
|
||||||
|
|
||||||
# sanity/coverage check using /settings/
|
# sanity/coverage check using /settings/
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting',
|
model_name='Setting',
|
||||||
model_key='name',
|
model_key='name',
|
||||||
get_index_url=MagicMock(return_value='/settings/'),
|
get_index_url=MagicMock(return_value='/settings/'),
|
||||||
grid_columns=['name', 'value']):
|
grid_columns=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
response = view.index()
|
response = view.index()
|
||||||
|
|
||||||
# then again with data, to include view action url
|
# then again with data, to include view action url
|
||||||
|
@ -769,12 +768,12 @@ class TestMasterView(WebTestCase):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
# sanity/coverage check using /settings/new
|
# sanity/coverage check using /settings/new
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting',
|
model_name='Setting',
|
||||||
model_key='name',
|
model_key='name',
|
||||||
get_index_url=MagicMock(return_value='/settings/'),
|
get_index_url=MagicMock(return_value='/settings/'),
|
||||||
form_fields=['name', 'value']):
|
form_fields=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
|
|
||||||
# no setting yet
|
# no setting yet
|
||||||
self.assertIsNone(self.app.get_setting(self.session, 'foo.bar'))
|
self.assertIsNone(self.app.get_setting(self.session, 'foo.bar'))
|
||||||
|
@ -825,13 +824,13 @@ class TestMasterView(WebTestCase):
|
||||||
# sanity/coverage check using /settings/XXX
|
# sanity/coverage check using /settings/XXX
|
||||||
setting = {'name': 'foo.bar', 'value': 'baz'}
|
setting = {'name': 'foo.bar', 'value': 'baz'}
|
||||||
self.request.matchdict = {'name': 'foo.bar'}
|
self.request.matchdict = {'name': 'foo.bar'}
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting',
|
model_name='Setting',
|
||||||
model_key='name',
|
model_key='name',
|
||||||
get_index_url=MagicMock(return_value='/settings/'),
|
get_index_url=MagicMock(return_value='/settings/'),
|
||||||
grid_columns=['name', 'value'],
|
grid_columns=['name', 'value'],
|
||||||
form_fields=['name', 'value']):
|
form_fields=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
with patch.object(view, 'get_instance', return_value=setting):
|
with patch.object(view, 'get_instance', return_value=setting):
|
||||||
response = view.view()
|
response = view.view()
|
||||||
|
|
||||||
|
@ -854,12 +853,12 @@ class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
# sanity/coverage check using /settings/XXX/edit
|
# sanity/coverage check using /settings/XXX/edit
|
||||||
self.request.matchdict = {'name': 'foo.bar'}
|
self.request.matchdict = {'name': 'foo.bar'}
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting',
|
model_name='Setting',
|
||||||
model_key='name',
|
model_key='name',
|
||||||
get_index_url=MagicMock(return_value='/settings/'),
|
get_index_url=MagicMock(return_value='/settings/'),
|
||||||
form_fields=['name', 'value']):
|
form_fields=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
with patch.object(view, 'get_instance', new=get_instance):
|
with patch.object(view, 'get_instance', new=get_instance):
|
||||||
|
|
||||||
# get the form page
|
# get the form page
|
||||||
|
@ -918,12 +917,12 @@ class TestMasterView(WebTestCase):
|
||||||
|
|
||||||
# sanity/coverage check using /settings/XXX/delete
|
# sanity/coverage check using /settings/XXX/delete
|
||||||
self.request.matchdict = {'name': 'foo.bar'}
|
self.request.matchdict = {'name': 'foo.bar'}
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='Setting',
|
model_name='Setting',
|
||||||
model_key='name',
|
model_key='name',
|
||||||
get_index_url=MagicMock(return_value='/settings/'),
|
get_index_url=MagicMock(return_value='/settings/'),
|
||||||
form_fields=['name', 'value']):
|
form_fields=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
with patch.object(view, 'get_instance', new=get_instance):
|
with patch.object(view, 'get_instance', new=get_instance):
|
||||||
|
|
||||||
# get the form page
|
# get the form page
|
||||||
|
@ -960,14 +959,55 @@ class TestMasterView(WebTestCase):
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
setting = self.session.query(model.Setting).one()
|
setting = self.session.query(model.Setting).one()
|
||||||
|
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_class=model.Setting,
|
model_class=model.Setting,
|
||||||
form_fields=['name', 'value']):
|
form_fields=['name', 'value']):
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
view.delete_instance(setting)
|
view.delete_instance(setting)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.assertEqual(self.session.query(model.Setting).count(), 0)
|
self.assertEqual(self.session.query(model.Setting).count(), 0)
|
||||||
|
|
||||||
|
def test_autocomplete(self):
|
||||||
|
model = self.app.model
|
||||||
|
|
||||||
|
person1 = model.Person(full_name="George Jones")
|
||||||
|
self.session.add(person1)
|
||||||
|
person2 = model.Person(full_name="George Strait")
|
||||||
|
self.session.add(person2)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
# no results for empty term
|
||||||
|
self.request.GET = {}
|
||||||
|
view = self.make_view()
|
||||||
|
results = view.autocomplete()
|
||||||
|
self.assertEqual(len(results), 0)
|
||||||
|
|
||||||
|
# search yields no results
|
||||||
|
self.request.GET = {'term': 'sally'}
|
||||||
|
view = self.make_view()
|
||||||
|
with patch.object(view, 'autocomplete_data', return_value=[]):
|
||||||
|
view = self.make_view()
|
||||||
|
results = view.autocomplete()
|
||||||
|
self.assertEqual(len(results), 0)
|
||||||
|
|
||||||
|
# search yields 2 results
|
||||||
|
self.request.GET = {'term': 'george'}
|
||||||
|
view = self.make_view()
|
||||||
|
with patch.object(view, 'autocomplete_data', return_value=[person1, person2]):
|
||||||
|
results = view.autocomplete()
|
||||||
|
self.assertEqual(len(results), 2)
|
||||||
|
self.assertEqual([res['value'] for res in results],
|
||||||
|
[p.uuid for p in [person1, person2]])
|
||||||
|
|
||||||
|
def test_autocomplete_normalize(self):
|
||||||
|
model = self.app.model
|
||||||
|
view = self.make_view()
|
||||||
|
|
||||||
|
person = model.Person(full_name="Betty Boop", uuid='bogus')
|
||||||
|
normal = view.autocomplete_normalize(person)
|
||||||
|
self.assertEqual(normal, {'value': 'bogus',
|
||||||
|
'label': "Betty Boop"})
|
||||||
|
|
||||||
def test_configure(self):
|
def test_configure(self):
|
||||||
self.pyramid_config.include('wuttaweb.views.common')
|
self.pyramid_config.include('wuttaweb.views.common')
|
||||||
self.pyramid_config.include('wuttaweb.views.auth')
|
self.pyramid_config.include('wuttaweb.views.auth')
|
||||||
|
@ -983,10 +1023,10 @@ class TestMasterView(WebTestCase):
|
||||||
{'name': 'wutta.value2', 'save_if_empty': False},
|
{'name': 'wutta.value2', 'save_if_empty': False},
|
||||||
]
|
]
|
||||||
|
|
||||||
view = master.MasterView(self.request)
|
view = mod.MasterView(self.request)
|
||||||
with patch.object(self.request, 'current_route_url', return_value='/appinfo/configure'):
|
with patch.object(self.request, 'current_route_url', return_value='/appinfo/configure'):
|
||||||
with patch.object(master, 'Session', return_value=self.session):
|
with patch.object(mod, 'Session', return_value=self.session):
|
||||||
with patch.multiple(master.MasterView, create=True,
|
with patch.multiple(mod.MasterView, create=True,
|
||||||
model_name='AppInfo',
|
model_name='AppInfo',
|
||||||
route_prefix='appinfo',
|
route_prefix='appinfo',
|
||||||
template_prefix='/appinfo',
|
template_prefix='/appinfo',
|
||||||
|
|
|
@ -41,6 +41,30 @@ class TestPersonView(WebTestCase):
|
||||||
self.assertTrue(form.required_fields)
|
self.assertTrue(form.required_fields)
|
||||||
self.assertFalse(form.required_fields['middle_name'])
|
self.assertFalse(form.required_fields['middle_name'])
|
||||||
|
|
||||||
|
def test_autocomplete_query(self):
|
||||||
|
model = self.app.model
|
||||||
|
|
||||||
|
person1 = model.Person(full_name="George Jones")
|
||||||
|
self.session.add(person1)
|
||||||
|
person2 = model.Person(full_name="George Strait")
|
||||||
|
self.session.add(person2)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
view = self.make_view()
|
||||||
|
with patch.object(view, 'Session', return_value=self.session):
|
||||||
|
|
||||||
|
# both people match
|
||||||
|
query = view.autocomplete_query('george')
|
||||||
|
self.assertEqual(query.count(), 2)
|
||||||
|
|
||||||
|
# just 1 match
|
||||||
|
query = view.autocomplete_query('jones')
|
||||||
|
self.assertEqual(query.count(), 1)
|
||||||
|
|
||||||
|
# no matches
|
||||||
|
query = view.autocomplete_query('sally')
|
||||||
|
self.assertEqual(query.count(), 0)
|
||||||
|
|
||||||
def test_view_profile(self):
|
def test_view_profile(self):
|
||||||
self.pyramid_config.include('wuttaweb.views.common')
|
self.pyramid_config.include('wuttaweb.views.common')
|
||||||
self.pyramid_config.include('wuttaweb.views.auth')
|
self.pyramid_config.include('wuttaweb.views.auth')
|
||||||
|
|
|
@ -67,6 +67,14 @@ class TestSettingView(WebTestCase):
|
||||||
data = query.all()
|
data = query.all()
|
||||||
self.assertEqual(len(data), 1)
|
self.assertEqual(len(data), 1)
|
||||||
|
|
||||||
|
def test_configure_grid(self):
|
||||||
|
model = self.app.model
|
||||||
|
view = self.make_view()
|
||||||
|
grid = view.make_grid(model_class=model.Setting)
|
||||||
|
self.assertFalse(grid.is_linked('name'))
|
||||||
|
view.configure_grid(grid)
|
||||||
|
self.assertTrue(grid.is_linked('name'))
|
||||||
|
|
||||||
def test_configure_form(self):
|
def test_configure_form(self):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
form = view.make_form(fields=view.get_form_fields())
|
form = view.make_form(fields=view.get_form_fields())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue