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
	
	 Lance Edgar
						Lance Edgar