fix: make some tweaks for better tailbone compatibility
this is the result of minimally testing the PersonView from wutta, configured via a tailbone app. had to add the `view_profile()` stub, pretty sure we want that..?
This commit is contained in:
parent
058632ebeb
commit
be8a45e543
|
@ -228,9 +228,9 @@ def add_permission(pyramid_config, groupkey, key, label=None):
|
||||||
|
|
||||||
See also :func:`add_permission_group()`.
|
See also :func:`add_permission_group()`.
|
||||||
"""
|
"""
|
||||||
config = pyramid_config.get_settings()['wutta_config']
|
|
||||||
app = config.get_app()
|
|
||||||
def action():
|
def action():
|
||||||
|
config = pyramid_config.get_settings()['wutta_config']
|
||||||
|
app = config.get_app()
|
||||||
perms = pyramid_config.get_settings().get('wutta_permissions', {})
|
perms = pyramid_config.get_settings().get('wutta_permissions', {})
|
||||||
group = perms.setdefault(groupkey, {'key': groupkey})
|
group = perms.setdefault(groupkey, {'key': groupkey})
|
||||||
group.setdefault('label', app.make_title(groupkey))
|
group.setdefault('label', app.make_title(groupkey))
|
||||||
|
|
|
@ -939,6 +939,20 @@ class Form:
|
||||||
|
|
||||||
return model_data
|
return model_data
|
||||||
|
|
||||||
|
# TODO: for tailbone compat, should document?
|
||||||
|
# (ideally should remove this and find a better way)
|
||||||
|
def get_vue_field_value(self, key):
|
||||||
|
""" """
|
||||||
|
if key not in self.fields:
|
||||||
|
return
|
||||||
|
|
||||||
|
dform = self.get_deform()
|
||||||
|
if key not in dform:
|
||||||
|
return
|
||||||
|
|
||||||
|
field = dform[key]
|
||||||
|
return make_json_safe(field.cstruct)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
"""
|
"""
|
||||||
Try to validate the form, using data from the :attr:`request`.
|
Try to validate the form, using data from the :attr:`request`.
|
||||||
|
|
|
@ -552,7 +552,7 @@ class GridAction:
|
||||||
See also :meth:`render_icon_and_label()`.
|
See also :meth:`render_icon_and_label()`.
|
||||||
"""
|
"""
|
||||||
if self.request.use_oruga:
|
if self.request.use_oruga:
|
||||||
raise NotImplementedError
|
return HTML.tag('o-icon', icon=self.icon)
|
||||||
|
|
||||||
return HTML.tag('i', class_=f'fas fa-{self.icon}')
|
return HTML.tag('i', class_=f'fas fa-{self.icon}')
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,19 @@
|
||||||
|
|
||||||
<%def name="render_this_page_template()">
|
<%def name="render_this_page_template()">
|
||||||
${parent.render_this_page_template()}
|
${parent.render_this_page_template()}
|
||||||
${form.render_vue_template()}
|
% if form is not Undefined:
|
||||||
|
${form.render_vue_template()}
|
||||||
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="finalize_this_page_vars()">
|
<%def name="finalize_this_page_vars()">
|
||||||
${parent.finalize_this_page_vars()}
|
${parent.finalize_this_page_vars()}
|
||||||
<script>
|
% if form is not Undefined:
|
||||||
${form.vue_component}.data = function() { return ${form.vue_component}Data }
|
<script>
|
||||||
Vue.component('${form.vue_tagname}', ${form.vue_component})
|
${form.vue_component}.data = function() { return ${form.vue_component}Data }
|
||||||
</script>
|
Vue.component('${form.vue_tagname}', ${form.vue_component})
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
|
12
src/wuttaweb/templates/people/view_profile.mako
Normal file
12
src/wuttaweb/templates/people/view_profile.mako
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
<p class="block is-size-5">
|
||||||
|
TODO: view profile page content
|
||||||
|
</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="title()">TODO: title</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -242,6 +242,9 @@ class MasterView(View):
|
||||||
deleting = False
|
deleting = False
|
||||||
configuring = False
|
configuring = False
|
||||||
|
|
||||||
|
# default DB session
|
||||||
|
Session = Session
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# index methods
|
# index methods
|
||||||
##############################
|
##############################
|
||||||
|
@ -305,7 +308,7 @@ class MasterView(View):
|
||||||
|
|
||||||
if form.validate():
|
if form.validate():
|
||||||
obj = self.create_save_form(form)
|
obj = self.create_save_form(form)
|
||||||
Session.flush()
|
self.Session.flush()
|
||||||
return self.redirect(self.get_action_url('view', obj))
|
return self.redirect(self.get_action_url('view', obj))
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
|
@ -362,7 +365,6 @@ class MasterView(View):
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'instance': instance,
|
'instance': instance,
|
||||||
'instance_title': self.get_instance_title(instance),
|
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
return self.render_to_response('view', context)
|
return self.render_to_response('view', context)
|
||||||
|
@ -396,7 +398,6 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
self.editing = True
|
self.editing = True
|
||||||
instance = self.get_instance()
|
instance = self.get_instance()
|
||||||
instance_title = self.get_instance_title(instance)
|
|
||||||
|
|
||||||
form = self.make_model_form(instance,
|
form = self.make_model_form(instance,
|
||||||
cancel_url_fallback=self.get_action_url('view', instance))
|
cancel_url_fallback=self.get_action_url('view', instance))
|
||||||
|
@ -407,7 +408,6 @@ class MasterView(View):
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'instance': instance,
|
'instance': instance,
|
||||||
'instance_title': instance_title,
|
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
return self.render_to_response('edit', context)
|
return self.render_to_response('edit', context)
|
||||||
|
@ -460,7 +460,6 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
self.deleting = True
|
self.deleting = True
|
||||||
instance = self.get_instance()
|
instance = self.get_instance()
|
||||||
instance_title = self.get_instance_title(instance)
|
|
||||||
|
|
||||||
if not self.is_deletable(instance):
|
if not self.is_deletable(instance):
|
||||||
return self.redirect(self.get_action_url('view', instance))
|
return self.redirect(self.get_action_url('view', instance))
|
||||||
|
@ -481,7 +480,6 @@ class MasterView(View):
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'instance': instance,
|
'instance': instance,
|
||||||
'instance_title': instance_title,
|
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
return self.render_to_response('delete', context)
|
return self.render_to_response('delete', context)
|
||||||
|
@ -516,7 +514,7 @@ class MasterView(View):
|
||||||
# configure methods
|
# configure methods
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
def configure(self):
|
def configure(self, session=None):
|
||||||
"""
|
"""
|
||||||
View for configuring aspects of the app which are pertinent to
|
View for configuring aspects of the app which are pertinent to
|
||||||
this master view and/or model.
|
this master view and/or model.
|
||||||
|
@ -562,7 +560,7 @@ class MasterView(View):
|
||||||
|
|
||||||
# maybe just remove settings
|
# maybe just remove settings
|
||||||
if self.request.POST.get('remove_settings'):
|
if self.request.POST.get('remove_settings'):
|
||||||
self.configure_remove_settings()
|
self.configure_remove_settings(session=session)
|
||||||
self.request.session.flash(f"All settings for {config_title} have been removed.",
|
self.request.session.flash(f"All settings for {config_title} have been removed.",
|
||||||
'warning')
|
'warning')
|
||||||
|
|
||||||
|
@ -572,8 +570,8 @@ class MasterView(View):
|
||||||
# gather/save settings
|
# gather/save settings
|
||||||
data = get_form_data(self.request)
|
data = get_form_data(self.request)
|
||||||
settings = self.configure_gather_settings(data)
|
settings = self.configure_gather_settings(data)
|
||||||
self.configure_remove_settings()
|
self.configure_remove_settings(session=session)
|
||||||
self.configure_save_settings(settings)
|
self.configure_save_settings(settings, session=session)
|
||||||
self.request.session.flash("Settings have been saved.")
|
self.request.session.flash("Settings have been saved.")
|
||||||
|
|
||||||
# reload configure page
|
# reload configure page
|
||||||
|
@ -751,6 +749,7 @@ class MasterView(View):
|
||||||
def configure_remove_settings(
|
def configure_remove_settings(
|
||||||
self,
|
self,
|
||||||
simple_settings=None,
|
simple_settings=None,
|
||||||
|
session=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Remove all "known" settings from the DB; this is called by
|
Remove all "known" settings from the DB; this is called by
|
||||||
|
@ -778,11 +777,11 @@ class MasterView(View):
|
||||||
if names:
|
if names:
|
||||||
# nb. must avoid self.Session here in case that does not
|
# nb. must avoid self.Session here in case that does not
|
||||||
# point to our primary app DB
|
# point to our primary app DB
|
||||||
session = Session()
|
session = session or self.Session()
|
||||||
for name in names:
|
for name in names:
|
||||||
self.app.delete_setting(session, name)
|
self.app.delete_setting(session, name)
|
||||||
|
|
||||||
def configure_save_settings(self, settings):
|
def configure_save_settings(self, settings, session=None):
|
||||||
"""
|
"""
|
||||||
Save the given settings to the DB; this is called by
|
Save the given settings to the DB; this is called by
|
||||||
:meth:`configure()`.
|
:meth:`configure()`.
|
||||||
|
@ -795,7 +794,7 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
# nb. must avoid self.Session here in case that does not point
|
# nb. must avoid self.Session here in case that does not point
|
||||||
# to our primary app DB
|
# to our primary app DB
|
||||||
session = Session()
|
session = session or self.Session()
|
||||||
for setting in settings:
|
for setting in settings:
|
||||||
self.app.save_setting(session, setting['name'], setting['value'],
|
self.app.save_setting(session, setting['name'], setting['value'],
|
||||||
force_create=True)
|
force_create=True)
|
||||||
|
@ -885,7 +884,13 @@ class MasterView(View):
|
||||||
|
|
||||||
# add crud flags if we have an instance
|
# add crud flags if we have an instance
|
||||||
if 'instance' in context:
|
if 'instance' in context:
|
||||||
context['instance_deletable'] = self.is_deletable(context['instance'])
|
instance = context['instance']
|
||||||
|
if 'instance_title' not in context:
|
||||||
|
context['instance_title'] = self.get_instance_title(instance)
|
||||||
|
if 'instance_editable' not in context:
|
||||||
|
context['instance_editable'] = self.is_editable(instance)
|
||||||
|
if 'instance_deletable' not in context:
|
||||||
|
context['instance_deletable'] = self.is_deletable(instance)
|
||||||
|
|
||||||
# first try the template path most specific to this view
|
# first try the template path most specific to this view
|
||||||
template_prefix = self.get_template_prefix()
|
template_prefix = self.get_template_prefix()
|
||||||
|
@ -1050,7 +1055,7 @@ class MasterView(View):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
model_class = self.get_model_class()
|
model_class = self.get_model_class()
|
||||||
if model_class and issubclass(model_class, model.Base):
|
if model_class and issubclass(model_class, model.Base):
|
||||||
session = session or Session()
|
session = session or self.Session()
|
||||||
return session.query(model_class)
|
return session.query(model_class)
|
||||||
|
|
||||||
def configure_grid(self, grid):
|
def configure_grid(self, grid):
|
||||||
|
@ -1110,7 +1115,7 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
model_class = self.get_model_class()
|
model_class = self.get_model_class()
|
||||||
if model_class:
|
if model_class:
|
||||||
session = session or Session()
|
session = session or self.Session()
|
||||||
|
|
||||||
def filtr(query, model_key):
|
def filtr(query, model_key):
|
||||||
key = self.request.matchdict[model_key]
|
key = self.request.matchdict[model_key]
|
||||||
|
@ -1361,7 +1366,7 @@ class MasterView(View):
|
||||||
if model_class and issubclass(model_class, model.Base):
|
if model_class and issubclass(model_class, model.Base):
|
||||||
|
|
||||||
# add sqlalchemy model to session
|
# add sqlalchemy model to session
|
||||||
session = session or Session()
|
session = session or self.Session()
|
||||||
session.add(obj)
|
session.add(obj)
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
|
|
|
@ -85,6 +85,33 @@ class PersonView(MasterView):
|
||||||
if 'users' in f:
|
if 'users' in f:
|
||||||
f.fields.remove('users')
|
f.fields.remove('users')
|
||||||
|
|
||||||
|
def view_profile(self, session=None):
|
||||||
|
""" """
|
||||||
|
instance = self.get_instance(session=session)
|
||||||
|
context = {
|
||||||
|
'instance': instance,
|
||||||
|
}
|
||||||
|
return self.render_to_response('view_profile', context)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
cls._defaults(config)
|
||||||
|
cls._people_defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _people_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
|
||||||
|
# view profile
|
||||||
|
config.add_route(f'{route_prefix}.view_profile',
|
||||||
|
f'{instance_url_prefix}/profile',
|
||||||
|
request_method='GET')
|
||||||
|
config.add_view(cls, attr='view_profile',
|
||||||
|
route_name=f'{route_prefix}.view_profile',
|
||||||
|
permission=f'{permission_prefix}.view_profile')
|
||||||
|
|
||||||
|
|
||||||
def defaults(config, **kwargs):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
base = globals()
|
||||||
|
|
|
@ -454,6 +454,28 @@ class TestForm(TestCase):
|
||||||
# nb. no error message
|
# nb. no error message
|
||||||
self.assertNotIn('message', html)
|
self.assertNotIn('message', html)
|
||||||
|
|
||||||
|
def test_get_vue_field_value(self):
|
||||||
|
schema = self.make_schema()
|
||||||
|
form = self.make_form(schema=schema)
|
||||||
|
|
||||||
|
# TODO: yikes what a hack (?)
|
||||||
|
dform = form.get_deform()
|
||||||
|
dform.set_appstruct({'foo': 'one', 'bar': 'two'})
|
||||||
|
|
||||||
|
# null for missing field
|
||||||
|
value = form.get_vue_field_value('doesnotexist')
|
||||||
|
self.assertIsNone(value)
|
||||||
|
|
||||||
|
# normal value is returned
|
||||||
|
value = form.get_vue_field_value('foo')
|
||||||
|
self.assertEqual(value, 'one')
|
||||||
|
|
||||||
|
# but not if we remove field from deform
|
||||||
|
# TODO: what is the use case here again?
|
||||||
|
dform.children.remove(dform['foo'])
|
||||||
|
value = form.get_vue_field_value('foo')
|
||||||
|
self.assertIsNone(value)
|
||||||
|
|
||||||
def test_get_vue_model_data(self):
|
def test_get_vue_model_data(self):
|
||||||
schema = self.make_schema()
|
schema = self.make_schema()
|
||||||
form = self.make_form(schema=schema)
|
form = self.make_form(schema=schema)
|
||||||
|
|
|
@ -190,9 +190,10 @@ class TestGridAction(TestCase):
|
||||||
html = action.render_icon()
|
html = action.render_icon()
|
||||||
self.assertIn('<i class="fas fa-blarg">', html)
|
self.assertIn('<i class="fas fa-blarg">', html)
|
||||||
|
|
||||||
# oruga not yet supported
|
# oruga has different output
|
||||||
self.request.use_oruga = True
|
self.request.use_oruga = True
|
||||||
self.assertRaises(NotImplementedError, action.render_icon)
|
html = action.render_icon()
|
||||||
|
self.assertIn('<o-icon icon="blarg">', html)
|
||||||
|
|
||||||
def test_render_label(self):
|
def test_render_label(self):
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from pyramid import testing
|
from pyramid import testing
|
||||||
from pyramid.response import Response
|
from pyramid.response import Response
|
||||||
from pyramid.httpexceptions import HTTPFound, 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
|
||||||
|
@ -942,7 +942,7 @@ class TestMasterView(WebTestCase):
|
||||||
configure_get_simple_settings=MagicMock(return_value=settings)):
|
configure_get_simple_settings=MagicMock(return_value=settings)):
|
||||||
|
|
||||||
# get the form page
|
# get the form page
|
||||||
response = view.configure()
|
response = view.configure(session=self.session)
|
||||||
self.assertIsInstance(response, Response)
|
self.assertIsInstance(response, Response)
|
||||||
|
|
||||||
# post request to save settings
|
# post request to save settings
|
||||||
|
@ -952,9 +952,9 @@ class TestMasterView(WebTestCase):
|
||||||
'wutta.foo': 'bar',
|
'wutta.foo': 'bar',
|
||||||
'wutta.flag': 'true',
|
'wutta.flag': 'true',
|
||||||
}
|
}
|
||||||
response = view.configure()
|
response = view.configure(session=self.session)
|
||||||
# nb. should get redirect back to configure page
|
# nb. should get redirect back to configure page
|
||||||
self.assertIsInstance(response, HTTPFound)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
# should now have 5 settings
|
# should now have 5 settings
|
||||||
count = self.session.query(model.Setting).count()
|
count = self.session.query(model.Setting).count()
|
||||||
|
@ -970,9 +970,9 @@ class TestMasterView(WebTestCase):
|
||||||
# post request to remove settings
|
# post request to remove settings
|
||||||
self.request.method = 'POST'
|
self.request.method = 'POST'
|
||||||
self.request.POST = {'remove_settings': '1'}
|
self.request.POST = {'remove_settings': '1'}
|
||||||
response = view.configure()
|
response = view.configure(session=self.session)
|
||||||
# nb. should get redirect back to configure page
|
# nb. should get redirect back to configure page
|
||||||
self.assertIsInstance(response, HTTPFound)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
# should now have 0 settings
|
# should now have 0 settings
|
||||||
count = self.session.query(model.Setting).count()
|
count = self.session.query(model.Setting).count()
|
||||||
|
|
|
@ -15,6 +15,9 @@ class TestPersonView(WebTestCase):
|
||||||
def make_view(self):
|
def make_view(self):
|
||||||
return people.PersonView(self.request)
|
return people.PersonView(self.request)
|
||||||
|
|
||||||
|
def test_includeme(self):
|
||||||
|
self.pyramid_config.include('wuttaweb.views.people')
|
||||||
|
|
||||||
def test_get_query(self):
|
def test_get_query(self):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
query = view.get_query(session=self.session)
|
query = view.get_query(session=self.session)
|
||||||
|
@ -37,3 +40,19 @@ class TestPersonView(WebTestCase):
|
||||||
view.configure_form(form)
|
view.configure_form(form)
|
||||||
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_view_profile(self):
|
||||||
|
self.pyramid_config.include('wuttaweb.views.common')
|
||||||
|
self.pyramid_config.include('wuttaweb.views.auth')
|
||||||
|
self.pyramid_config.add_route('people', '/people/')
|
||||||
|
|
||||||
|
model = self.app.model
|
||||||
|
person = model.Person(full_name="Barney Rubble")
|
||||||
|
self.session.add(person)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
# sanity check
|
||||||
|
view = self.make_view()
|
||||||
|
self.request.matchdict = {'uuid': person.uuid}
|
||||||
|
response = view.view_profile(session=self.session)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
Loading…
Reference in a new issue