3
0
Fork 0

fix: remove session param from some form schema, widget classes

this was originally used for injecting the test session, but i wound
up using mock instead elsewhere, so this is just for consistency
This commit is contained in:
Lance Edgar 2025-01-02 21:09:31 -06:00
parent a612bf3846
commit a219f3e30d
4 changed files with 214 additions and 201 deletions

View file

@ -181,21 +181,16 @@ class WuttaSet(colander.Set):
"""
Custom schema type for :class:`python:set` fields.
This is a subclass of :class:`colander.Set`, but adds
Wutta-related params to the constructor.
This is a subclass of :class:`colander.Set`.
:param request: Current :term:`request` object.
:param session: Optional :term:`db session` to use instead of
:class:`wuttaweb.db.sess.Session`.
"""
def __init__(self, request, session=None):
def __init__(self, request):
super().__init__()
self.request = request
self.config = self.request.wutta_config
self.app = self.config.get_app()
self.session = session or Session()
class ObjectRef(colander.SchemaType):
@ -231,7 +226,6 @@ class ObjectRef(colander.SchemaType):
self,
request,
empty_option=None,
session=None,
*args,
**kwargs,
):
@ -240,7 +234,7 @@ class ObjectRef(colander.SchemaType):
self.config = self.request.wutta_config
self.app = self.config.get_app()
self.model_instance = None
self.session = session or Session()
self.session = Session()
if empty_option:
if empty_option is True:
@ -472,7 +466,7 @@ class RoleRefs(WuttaSet):
:returns: Instance of
:class:`~wuttaweb.forms.widgets.RoleRefsWidget`.
"""
kwargs.setdefault('session', self.session)
session = kwargs.setdefault('session', Session())
if 'values' not in kwargs:
model = self.app.model
@ -480,20 +474,20 @@ class RoleRefs(WuttaSet):
# avoid built-ins which cannot be assigned to users
avoid = {
auth.get_role_authenticated(self.session),
auth.get_role_anonymous(self.session),
auth.get_role_authenticated(session),
auth.get_role_anonymous(session),
}
avoid = set([role.uuid for role in avoid])
# also avoid admin unless current user is root
if not self.request.is_root:
avoid.add(auth.get_role_administrator(self.session).uuid)
avoid.add(auth.get_role_administrator(session).uuid)
# everything else can be (un)assigned for users
roles = self.session.query(model.Role)\
.filter(~model.Role.uuid.in_(avoid))\
.order_by(model.Role.name)\
.all()
roles = session.query(model.Role)\
.filter(~model.Role.uuid.in_(avoid))\
.order_by(model.Role.name)\
.all()
values = [(role.uuid.hex, role.name) for role in roles]
kwargs['values'] = values
@ -518,7 +512,7 @@ class UserRefs(WuttaSet):
:returns: Instance of
:class:`~wuttaweb.forms.widgets.UserRefsWidget`.
"""
kwargs.setdefault('session', self.session)
kwargs.setdefault('session', Session())
return widgets.UserRefsWidget(self.request, **kwargs)
@ -548,7 +542,7 @@ class Permissions(WuttaSet):
:returns: Instance of
:class:`~wuttaweb.forms.widgets.PermissionsWidget`.
"""
kwargs.setdefault('session', self.session)
kwargs.setdefault('session', Session())
kwargs.setdefault('permissions', self.permissions)
if 'values' not in kwargs:

View file

@ -136,26 +136,21 @@ class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
Custom widget for :class:`python:set` fields.
This is a subclass of
:class:`deform:deform.widget.CheckboxChoiceWidget`, but adds
Wutta-related params to the constructor.
:class:`deform:deform.widget.CheckboxChoiceWidget`.
:param request: Current :term:`request` object.
:param session: Optional :term:`db session` to use instead of
:class:`wuttaweb.db.sess.Session`.
It uses these Deform templates:
* ``checkbox_choice``
* ``readonly/checkbox_choice``
"""
def __init__(self, request, session=None, *args, **kwargs):
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
self.config = self.request.wutta_config
self.app = self.config.get_app()
self.session = session or Session()
class WuttaDateTimeWidget(DateTimeInputWidget):

View file

@ -155,9 +155,10 @@ class TestObjectRef(DataTestCase):
self.session.commit()
self.assertIsNotNone(person.uuid)
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
value = typ.deserialize(node, person.uuid)
self.assertIs(value, person)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.ObjectRef(self.request)
value = typ.deserialize(node, person.uuid)
self.assertIs(value, person)
def test_dictify(self):
model = self.app.model
@ -181,42 +182,46 @@ class TestObjectRef(DataTestCase):
value = typ.objectify(None)
self.assertIsNone(value)
# model instance
person = model.Person(full_name="Betty Boop")
self.session.add(person)
self.session.commit()
self.assertIsNotNone(person.uuid)
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
with patch.object(mod, 'Session', return_value=self.session):
# can specify as uuid
typ = mod.ObjectRef(self.request, session=self.session)
value = typ.objectify(person.uuid)
self.assertIs(value, person)
# model instance
person = model.Person(full_name="Betty Boop")
self.session.add(person)
self.session.commit()
self.assertIsNotNone(person.uuid)
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
# or can specify object proper
typ = mod.ObjectRef(self.request, session=self.session)
value = typ.objectify(person)
self.assertIs(value, person)
# can specify as uuid
typ = mod.ObjectRef(self.request)
value = typ.objectify(person.uuid)
self.assertIs(value, person)
# error if not found
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
self.assertRaises(ValueError, typ.objectify, 'WRONG-UUID')
# or can specify object proper
typ = mod.ObjectRef(self.request)
value = typ.objectify(person)
self.assertIs(value, person)
# error if not found
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request)
self.assertRaises(ValueError, typ.objectify, 'WRONG-UUID')
def test_get_query(self):
model = self.app.model
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.ObjectRef(self.request)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
def test_sort_query(self):
model = self.app.model
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
query = typ.get_query()
sorted_query = typ.sort_query(query)
self.assertIs(sorted_query, query)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.ObjectRef(self.request)
query = typ.get_query()
sorted_query = typ.sort_query(query)
self.assertIs(sorted_query, query)
def test_widget_maker(self):
model = self.app.model
@ -226,90 +231,98 @@ class TestObjectRef(DataTestCase):
# basic
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 1)
self.assertEqual(widget.values[0][1], "Betty Boop")
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.ObjectRef(self.request)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 1)
self.assertEqual(widget.values[0][1], "Betty Boop")
# empty option
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session, empty_option=True)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 2)
self.assertEqual(widget.values[0][1], "(none)")
self.assertEqual(widget.values[1][1], "Betty Boop")
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.ObjectRef(self.request, empty_option=True)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 2)
self.assertEqual(widget.values[0][1], "(none)")
self.assertEqual(widget.values[1][1], "Betty Boop")
class TestPersonRef(WebTestCase):
def test_sort_query(self):
typ = mod.PersonRef(self.request, session=self.session)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.PersonRef(self.request)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
def test_get_object_url(self):
self.pyramid_config.add_route('people.view', '/people/{uuid}')
model = self.app.model
typ = mod.PersonRef(self.request, session=self.session)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.PersonRef(self.request)
person = model.Person(full_name="Barney Rubble")
self.session.add(person)
self.session.commit()
person = model.Person(full_name="Barney Rubble")
self.session.add(person)
self.session.commit()
url = typ.get_object_url(person)
self.assertIsNotNone(url)
self.assertIn(f'/people/{person.uuid}', url)
url = typ.get_object_url(person)
self.assertIsNotNone(url)
self.assertIn(f'/people/{person.uuid}', url)
class TestRoleRef(WebTestCase):
def test_sort_query(self):
typ = mod.RoleRef(self.request, session=self.session)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.RoleRef(self.request)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
def test_get_object_url(self):
self.pyramid_config.add_route('roles.view', '/roles/{uuid}')
model = self.app.model
typ = mod.RoleRef(self.request, session=self.session)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.RoleRef(self.request)
role = model.Role(name='Manager')
self.session.add(role)
self.session.commit()
role = model.Role(name='Manager')
self.session.add(role)
self.session.commit()
url = typ.get_object_url(role)
self.assertIsNotNone(url)
self.assertIn(f'/roles/{role.uuid}', url)
url = typ.get_object_url(role)
self.assertIsNotNone(url)
self.assertIn(f'/roles/{role.uuid}', url)
class TestUserRef(WebTestCase):
def test_sort_query(self):
typ = mod.UserRef(self.request, session=self.session)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.UserRef(self.request)
query = typ.get_query()
self.assertIsInstance(query, orm.Query)
sorted_query = typ.sort_query(query)
self.assertIsInstance(sorted_query, orm.Query)
self.assertIsNot(sorted_query, query)
def test_get_object_url(self):
self.pyramid_config.add_route('users.view', '/users/{uuid}')
model = self.app.model
typ = mod.UserRef(self.request, session=self.session)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.UserRef(self.request)
user = model.User(username='barney')
self.session.add(user)
self.session.commit()
user = model.User(username='barney')
self.session.add(user)
self.session.commit()
url = typ.get_object_url(user)
self.assertIsNotNone(url)
self.assertIn(f'/users/{user.uuid}', url)
url = typ.get_object_url(user)
self.assertIsNotNone(url)
self.assertIn(f'/users/{user.uuid}', url)
class TestUserRefs(DataTestCase):
@ -320,9 +333,10 @@ class TestUserRefs(DataTestCase):
def test_widget_maker(self):
model = self.app.model
typ = mod.UserRefs(self.request, session=self.session)
widget = typ.widget_maker()
self.assertIsInstance(widget, widgets.UserRefsWidget)
with patch.object(mod, 'Session', return_value=self.session):
typ = mod.UserRefs(self.request)
widget = typ.widget_maker()
self.assertIsInstance(widget, widgets.UserRefsWidget)
class TestRoleRefs(DataTestCase):
@ -341,20 +355,22 @@ class TestRoleRefs(DataTestCase):
self.session.add(blokes)
self.session.commit()
# with root access, default values include: admin, blokes
self.request.is_root = True
typ = mod.RoleRefs(self.request, session=self.session)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 2)
self.assertEqual(widget.values[0][1], "Administrator")
self.assertEqual(widget.values[1][1], "Blokes")
with patch.object(mod, 'Session', return_value=self.session):
# without root, default values include: blokes
self.request.is_root = False
typ = mod.RoleRefs(self.request, session=self.session)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 1)
self.assertEqual(widget.values[0][1], "Blokes")
# with root access, default values include: admin, blokes
self.request.is_root = True
typ = mod.RoleRefs(self.request)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 2)
self.assertEqual(widget.values[0][1], "Administrator")
self.assertEqual(widget.values[1][1], "Blokes")
# without root, default values include: blokes
self.request.is_root = False
typ = mod.RoleRefs(self.request)
widget = typ.widget_maker()
self.assertEqual(len(widget.values), 1)
self.assertEqual(widget.values[0][1], "Blokes")
class TestPermissions(DataTestCase):

View file

@ -10,6 +10,7 @@ from pyramid import testing
from wuttaweb import grids
from wuttaweb.forms import widgets as mod
from wuttaweb.forms import schema
from wuttaweb.forms.schema import (FileDownload, PersonRef, RoleRefs, UserRefs, Permissions,
WuttaDateTime, EmailRecipients)
from tests.util import WebTestCase
@ -32,31 +33,33 @@ class TestObjectRefWidget(WebTestCase):
self.session.add(person)
self.session.commit()
# standard (editable)
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
widget = self.make_widget()
field = self.make_field(node)
html = widget.serialize(field, person.uuid)
self.assertIn('<b-select ', html)
with patch.object(schema, 'Session', return_value=self.session):
# readonly
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
node.model_instance = person
widget = self.make_widget()
field = self.make_field(node)
html = widget.serialize(field, person.uuid, readonly=True)
self.assertIn('Betty Boop', html)
self.assertNotIn('<a', html)
# standard (editable)
node = colander.SchemaNode(PersonRef(self.request))
widget = self.make_widget()
field = self.make_field(node)
html = widget.serialize(field, person.uuid)
self.assertIn('<b-select ', html)
# with hyperlink
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
node.model_instance = person
widget = self.make_widget(url=lambda p: '/foo')
field = self.make_field(node)
html = widget.serialize(field, person.uuid, readonly=True)
self.assertIn('Betty Boop', html)
self.assertIn('<a', html)
self.assertIn('href="/foo"', html)
# readonly
node = colander.SchemaNode(PersonRef(self.request))
node.model_instance = person
widget = self.make_widget()
field = self.make_field(node)
html = widget.serialize(field, person.uuid, readonly=True)
self.assertIn('Betty Boop', html)
self.assertNotIn('<a', html)
# with hyperlink
node = colander.SchemaNode(PersonRef(self.request))
node.model_instance = person
widget = self.make_widget(url=lambda p: '/foo')
field = self.make_field(node)
html = widget.serialize(field, person.uuid, readonly=True)
self.assertIn('Betty Boop', html)
self.assertIn('<a', html)
self.assertIn('href="/foo"', html)
def test_get_template_values(self):
model = self.app.model
@ -64,22 +67,24 @@ class TestObjectRefWidget(WebTestCase):
self.session.add(person)
self.session.commit()
# standard
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
widget = self.make_widget()
field = self.make_field(node)
values = widget.get_template_values(field, person.uuid, {})
self.assertIn('cstruct', values)
self.assertNotIn('url', values)
with patch.object(schema, 'Session', return_value=self.session):
# readonly w/ empty option
node = colander.SchemaNode(PersonRef(self.request, session=self.session,
empty_option=('_empty_', '(empty)')))
widget = self.make_widget(readonly=True, url=lambda obj: '/foo')
field = self.make_field(node)
values = widget.get_template_values(field, '_empty_', {})
self.assertIn('cstruct', values)
self.assertNotIn('url', values)
# standard
node = colander.SchemaNode(PersonRef(self.request))
widget = self.make_widget()
field = self.make_field(node)
values = widget.get_template_values(field, person.uuid, {})
self.assertIn('cstruct', values)
self.assertNotIn('url', values)
# readonly w/ empty option
node = colander.SchemaNode(PersonRef(self.request,
empty_option=('_empty_', '(empty)')))
widget = self.make_widget(readonly=True, url=lambda obj: '/foo')
field = self.make_field(node)
values = widget.get_template_values(field, '_empty_', {})
self.assertIn('cstruct', values)
self.assertNotIn('url', values)
class TestWuttaDateTimeWidget(WebTestCase):
@ -230,28 +235,29 @@ class TestRoleRefsWidget(WebTestCase):
self.session.commit()
# nb. we let the field construct the widget via our type
node = colander.SchemaNode(RoleRefs(self.request, session=self.session))
field = self.make_field(node)
widget = field.widget
with patch.object(schema, 'Session', return_value=self.session):
node = colander.SchemaNode(RoleRefs(self.request))
field = self.make_field(node)
widget = field.widget
# readonly values list includes admin
html = widget.serialize(field, {admin.uuid, blokes.uuid}, readonly=True)
self.assertIn(admin.name, html)
self.assertIn(blokes.name, html)
# readonly values list includes admin
html = widget.serialize(field, {admin.uuid, blokes.uuid}, readonly=True)
self.assertIn(admin.name, html)
self.assertIn(blokes.name, html)
# editable values list *excludes* admin (by default)
html = widget.serialize(field, {admin.uuid, blokes.uuid})
self.assertNotIn(str(admin.uuid.hex), html)
self.assertIn(str(blokes.uuid.hex), html)
# editable values list *excludes* admin (by default)
html = widget.serialize(field, {admin.uuid, blokes.uuid})
self.assertNotIn(str(admin.uuid.hex), html)
self.assertIn(str(blokes.uuid.hex), html)
# but admin is included for root user
self.request.is_root = True
node = colander.SchemaNode(RoleRefs(self.request, session=self.session))
field = self.make_field(node)
widget = field.widget
html = widget.serialize(field, {admin.uuid, blokes.uuid})
self.assertIn(str(admin.uuid.hex), html)
self.assertIn(str(blokes.uuid.hex), html)
# but admin is included for root user
self.request.is_root = True
node = colander.SchemaNode(RoleRefs(self.request))
field = self.make_field(node)
widget = field.widget
html = widget.serialize(field, {admin.uuid, blokes.uuid})
self.assertIn(str(admin.uuid.hex), html)
self.assertIn(str(blokes.uuid.hex), html)
class TestUserRefsWidget(WebTestCase):
@ -266,35 +272,37 @@ class TestUserRefsWidget(WebTestCase):
model = self.app.model
# nb. we let the field construct the widget via our type
node = colander.SchemaNode(UserRefs(self.request, session=self.session))
field = self.make_field(node)
widget = field.widget
# node = colander.SchemaNode(UserRefs(self.request, session=self.session))
with patch.object(schema, 'Session', return_value=self.session):
node = colander.SchemaNode(UserRefs(self.request))
field = self.make_field(node)
widget = field.widget
# readonly is required
self.assertRaises(NotImplementedError, widget.serialize, field, set())
self.assertRaises(NotImplementedError, widget.serialize, field, set(), readonly=False)
# readonly is required
self.assertRaises(NotImplementedError, widget.serialize, field, set())
self.assertRaises(NotImplementedError, widget.serialize, field, set(), readonly=False)
# empty
html = widget.serialize(field, set(), readonly=True)
self.assertEqual(html, '<span></span>')
# empty
html = widget.serialize(field, set(), readonly=True)
self.assertEqual(html, '<span></span>')
# with data, no actions
user = model.User(username='barney')
self.session.add(user)
self.session.commit()
html = widget.serialize(field, {user.uuid}, readonly=True)
self.assertIn('<b-table ', html)
self.assertNotIn('Actions', html)
self.assertNotIn('View', html)
self.assertNotIn('Edit', html)
# with view/edit actions
with patch.object(self.request, 'is_root', new=True):
# with data, no actions
user = model.User(username='barney')
self.session.add(user)
self.session.commit()
html = widget.serialize(field, {user.uuid}, readonly=True)
self.assertIn('<b-table ', html)
self.assertIn('Actions', html)
self.assertIn('View', html)
self.assertIn('Edit', html)
self.assertIn('<b-table ', html)
self.assertNotIn('Actions', html)
self.assertNotIn('View', html)
self.assertNotIn('Edit', html)
# with view/edit actions
with patch.object(self.request, 'is_root', new=True):
html = widget.serialize(field, {user.uuid}, readonly=True)
self.assertIn('<b-table ', html)
self.assertIn('Actions', html)
self.assertIn('View', html)
self.assertIn('Edit', html)
class TestPermissionsWidget(WebTestCase):
@ -318,7 +326,7 @@ class TestPermissionsWidget(WebTestCase):
}
# nb. we let the field construct the widget via our type
node = colander.SchemaNode(Permissions(self.request, permissions, session=self.session))
node = colander.SchemaNode(Permissions(self.request, permissions))
field = self.make_field(node)
widget = field.widget