3
0
Fork 0

Compare commits

...

3 commits

Author SHA1 Message Date
Lance Edgar 6edaf578c3 bump: version 0.20.6 → 0.21.0 2025-02-01 15:16:57 -06:00
Lance Edgar 5b2d1dad53 feat: overhaul some User/Person form fields etc.
hoping this is more intuitive to use..
2025-01-27 17:07:42 -06:00
Lance Edgar 70ed2dc78c fix: do not auto-create grid filters for uuid columns 2025-01-27 15:55:07 -06:00
12 changed files with 322 additions and 226 deletions

View file

@ -5,6 +5,16 @@ All notable changes to wuttaweb will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## v0.21.0 (2025-02-01)
### Feat
- overhaul some User/Person form fields etc.
### Fix
- do not auto-create grid filters for uuid columns
## v0.20.6 (2025-01-26)
### Fix

View file

@ -6,7 +6,7 @@ build-backend = "hatchling.build"
[project]
name = "WuttaWeb"
version = "0.20.6"
version = "0.21.0"
description = "Web App for Wutta Framework"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@ -44,7 +44,7 @@ dependencies = [
"pyramid_tm",
"waitress",
"WebHelpers2",
"WuttJamaican[db]>=0.20.2",
"WuttJamaican[db]>=0.20.4",
"zope.sqlalchemy>=1.5",
]

View file

@ -572,28 +572,6 @@ class RoleRefs(WuttaSet):
return widgets.RoleRefsWidget(self.request, **kwargs)
class UserRefs(WuttaSet):
"""
Form schema type for the Role
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users`
association proxy field.
This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` ``uuid``
values for underlying data format.
"""
def widget_maker(self, **kwargs):
"""
Constructs a default widget for the field.
:returns: Instance of
:class:`~wuttaweb.forms.widgets.UserRefsWidget`.
"""
kwargs.setdefault('session', Session())
return widgets.UserRefsWidget(self.request, **kwargs)
class Permissions(WuttaSet):
"""
Form schema type for the Role

View file

@ -56,7 +56,6 @@ from webhelpers2.html import HTML
from wuttjamaican.conf import parse_list
from wuttaweb.db import Session
from wuttaweb.grids import Grid
class ObjectRefWidget(SelectWidget):
@ -414,63 +413,6 @@ class RoleRefsWidget(WuttaCheckboxChoiceWidget):
return super().serialize(field, cstruct, **kw)
class UserRefsWidget(WuttaCheckboxChoiceWidget):
"""
Widget for use with Role
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users` field.
This is the default widget for the
:class:`~wuttaweb.forms.schema.UserRefs` type.
This is a subclass of :class:`WuttaCheckboxChoiceWidget`; however
it only supports readonly mode and does not use a template.
Rather, it generates and renders a
:class:`~wuttaweb.grids.base.Grid` showing the users list.
"""
def serialize(self, field, cstruct, **kw):
""" """
readonly = kw.get('readonly', self.readonly)
if not readonly:
raise NotImplementedError("edit not allowed for this widget")
model = self.app.model
columns = ['username', 'active']
# generate data set for users
users = []
if cstruct:
for uuid in cstruct:
user = self.session.get(model.User, uuid)
if user:
users.append(dict([(key, getattr(user, key))
for key in columns + ['uuid']]))
# do not render if no data
if not users:
return HTML.tag('span')
# grid
grid = Grid(self.request, key='roles.view.users',
columns=columns, data=users)
# view action
if self.request.has_perm('users.view'):
url = lambda user, i: self.request.route_url('users.view', uuid=user['uuid'])
grid.add_action('view', icon='eye', url=url)
grid.set_link('person')
grid.set_link('username')
# edit action
if self.request.has_perm('users.edit'):
url = lambda user, i: self.request.route_url('users.edit', uuid=user['uuid'])
grid.add_action('edit', url=url)
# render as simple <b-table>
# nb. must indicate we are a part of this form
form = getattr(field.parent, 'wutta_form', None)
return grid.render_table_element(form)
class PermissionsWidget(WuttaCheckboxChoiceWidget):
"""
Widget for use with Role

View file

@ -41,6 +41,7 @@ from webhelpers2.html import HTML
from wuttaweb.db import Session
from wuttaweb.util import FieldList, get_model_fields, make_json_safe
from wuttjamaican.util import UNSPECIFIED
from wuttjamaican.db.util import UUID
from wuttaweb.grids.filters import default_sqlalchemy_filters, VerbNotSupported
@ -1136,14 +1137,15 @@ class Grid:
def make_backend_filters(self, filters=None):
"""
Make backend filters for all columns in the grid.
Make "automatic" backend filters for the grid.
This is called by the constructor, if :attr:`filterable` is
true.
For each column in the grid, this checks the provided
``filters`` and if the column is not yet in there, will call
:meth:`make_filter()` to add it.
For each "column" in the model class, this will call
:meth:`make_filter()` to add an automatic filter. However it
first checks the provided ``filters`` and will not override
any of those.
.. note::
@ -1181,9 +1183,18 @@ class Grid:
inspector = sa.inspect(self.model_class)
for prop in inspector.column_attrs:
if prop.key not in filters:
attr = getattr(self.model_class, prop.key)
filters[prop.key] = self.make_filter(attr)
# do not overwrite existing filters
if prop.key in filters:
continue
# do not create filter for UUID field
if (len(prop.columns) == 1
and isinstance(prop.columns[0].type, UUID)):
continue
attr = getattr(self.model_class, prop.key)
filters[prop.key] = self.make_filter(attr)
return filters

View file

@ -28,7 +28,6 @@ import sqlalchemy as sa
from wuttjamaican.db.model import Person
from wuttaweb.views import MasterView
from wuttaweb.forms.schema import UserRefs
class PersonView(MasterView):
@ -62,6 +61,14 @@ class PersonView(MasterView):
'full_name': {'active': True},
}
form_fields = [
'full_name',
'first_name',
'middle_name',
'last_name',
'users',
]
def configure_grid(self, g):
""" """
super().configure_grid(g)
@ -80,20 +87,54 @@ class PersonView(MasterView):
super().configure_form(f)
person = f.model_instance
# TODO: master should handle these? (nullable column)
f.set_required('first_name', False)
f.set_required('middle_name', False)
f.set_required('last_name', False)
# full_name
if self.creating or self.editing:
f.remove('full_name')
# users
# nb. colanderalchemy wants to do some magic for the true
# 'users' relationship, so we use a different field name
f.remove('users')
if not (self.creating or self.editing):
f.append('_users')
f.set_readonly('_users')
f.set_node('_users', UserRefs(self.request))
f.set_default('_users', [u.uuid for u in person.users])
if self.viewing:
f.set_grid('users', self.make_users_grid(person))
def make_users_grid(self, person):
"""
Make and return the grid for the Users field.
This grid is shown for the Users field when viewing a Person.
:returns: Fully configured :class:`~wuttaweb.grids.base.Grid`
instance.
"""
model = self.app.model
route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.users',
model_class=model.User,
data=person.users,
columns=[
'username',
'active',
])
if self.request.has_perm('users.view'):
url = lambda user, i: self.request.route_url('users.view', uuid=user.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('username')
if self.request.has_perm('users.edit'):
url = lambda user, i: self.request.route_url('users.edit', uuid=user.uuid)
grid.add_action('edit', url=url)
return grid
def objectify(self, form):
""" """
person = super().objectify(form)
# full_name
person.full_name = self.app.make_full_name(person.first_name,
person.last_name)
return person
def autocomplete_query(self, term):
""" """

View file

@ -2,7 +2,7 @@
################################################################################
#
# wuttaweb -- Web App for Wutta Framework
# Copyright © 2024 Lance Edgar
# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
@ -30,7 +30,6 @@ from wuttjamaican.db.model import User
from wuttaweb.views import MasterView
from wuttaweb.forms import widgets
from wuttaweb.forms.schema import PersonRef, RoleRefs
from wuttaweb.db import Session
class UserView(MasterView):
@ -61,6 +60,14 @@ class UserView(MasterView):
}
sort_defaults = 'username'
form_fields = [
'username',
'person',
'active',
'prevent_edit',
'roles',
]
def get_query(self, session=None):
""" """
query = super().get_query(session=session)
@ -109,17 +116,24 @@ class UserView(MasterView):
super().configure_form(f)
user = f.model_instance
# never show these
f.remove('person_uuid',
'role_refs')
# person
f.set_node('person', PersonRef(self.request, empty_option=True))
f.set_required('person', False)
# username
f.set_validator('username', self.unique_username)
# person
if self.creating or self.editing:
f.fields.insert_after('person', 'first_name')
f.set_required('first_name', False)
f.fields.insert_after('first_name', 'last_name')
f.set_required('last_name', False)
f.remove('person')
if self.editing:
person = user.person
if person:
f.set_default('first_name', person.first_name)
f.set_default('last_name', person.last_name)
else:
f.set_node('person', PersonRef(self.request))
# password
# nb. we must avoid 'password' as field name since
# ColanderAlchemy wants to handle the raw/hashed value
@ -140,7 +154,7 @@ class UserView(MasterView):
def unique_username(self, node, value):
""" """
model = self.app.model
session = Session()
session = self.Session()
query = session.query(model.User)\
.filter(model.User.username == value)
@ -152,26 +166,48 @@ class UserView(MasterView):
if query.count():
node.raise_invalid("Username must be unique")
def objectify(self, form, session=None):
def objectify(self, form):
""" """
model = self.app.model
auth = self.app.get_auth_handler()
data = form.validated
# normal logic first
user = super().objectify(form)
# maybe update person name
if 'first_name' in form or 'last_name' in form:
first_name = data.get('first_name')
last_name = data.get('last_name')
if self.creating and (first_name or last_name):
user.person = auth.make_person(first_name=first_name, last_name=last_name)
elif self.editing:
if first_name or last_name:
if user.person:
person = user.person
if 'first_name' in form:
person.first_name = first_name
if 'last_name' in form:
person.last_name = last_name
person.full_name = self.app.make_full_name(person.first_name,
person.last_name)
else:
user.person = auth.make_person(first_name=first_name, last_name=last_name)
elif user.person:
user.person = None
# maybe set user password
if 'set_password' in form and data.get('set_password'):
auth = self.app.get_auth_handler()
auth.set_user_password(user, data['set_password'])
# update roles for user
# TODO
# if self.has_perm('edit_roles'):
self.update_roles(user, form, session=session)
self.update_roles(user, form)
return user
def update_roles(self, user, form, session=None):
def update_roles(self, user, form):
""" """
# TODO
# if not self.has_perm('edit_roles'):
@ -181,7 +217,7 @@ class UserView(MasterView):
return
model = self.app.model
session = session or Session()
session = self.Session()
auth = self.app.get_auth_handler()
old_roles = set([role.uuid for role in user.roles])

View file

@ -377,20 +377,6 @@ class TestUserRef(WebTestCase):
self.assertIn(f'/users/{user.uuid}', url)
class TestUserRefs(DataTestCase):
def setUp(self):
self.setup_db()
self.request = testing.DummyRequest(wutta_config=self.config)
def test_widget_maker(self):
model = self.app.model
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):
def setUp(self):

View file

@ -11,7 +11,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,
from wuttaweb.forms.schema import (FileDownload, PersonRef, RoleRefs, Permissions,
WuttaDateTime, EmailRecipients)
from wuttaweb.testing import WebTestCase
@ -300,51 +300,6 @@ class TestRoleRefsWidget(WebTestCase):
self.assertIn(str(blokes.uuid.hex), html)
class TestUserRefsWidget(WebTestCase):
def make_field(self, node, **kwargs):
# TODO: not sure why default renderer is in use even though
# pyramid_deform was included in setup? but this works..
kwargs.setdefault('renderer', deform.Form.default_renderer)
return deform.Field(node, **kwargs)
def test_serialize(self):
model = self.app.model
# nb. we let the field construct the widget via our type
# 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)
# 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):
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):
def make_field(self, node, **kwargs):

View file

@ -1021,6 +1021,9 @@ class TestGrid(WebTestCase):
self.assertIn('active', filters)
# nb. relationship not included by default
self.assertNotIn('person', filters)
# nb. uuid fields not included by default
self.assertNotIn('uuid', filters)
self.assertNotIn('person_uuid', filters)
def test_make_filter(self):
model = self.app.model

View file

@ -8,6 +8,8 @@ from pyramid.httpexceptions import HTTPNotFound
from wuttaweb.views import people
from wuttaweb.testing import WebTestCase
from wuttaweb.forms.widgets import GridWidget
from wuttaweb.grids import Grid
class TestPersonView(WebTestCase):
@ -34,27 +36,59 @@ class TestPersonView(WebTestCase):
def test_configure_form(self):
model = self.app.model
view = self.make_view()
# full_name
form = view.make_form(model_class=model.Person)
# required fields
self.assertIn('full_name', form)
with patch.object(view, 'creating', new=True):
form.set_fields(form.get_model_fields())
self.assertEqual(form.required_fields, {})
view.configure_form(form)
self.assertTrue(form.required_fields)
self.assertFalse(form.required_fields['middle_name'])
self.assertNotIn('full_name', form)
person = model.Person(full_name="Barney Rubble")
user = model.User(username='barney', person=person)
self.session.add(user)
self.session.commit()
# users field
# users
person = model.Person()
form = view.make_form(model_instance=person)
self.assertNotIn('users', form.widgets)
with patch.object(view, 'viewing', new=True):
form = view.make_form(model_instance=person)
self.assertEqual(form.defaults, {})
view.configure_form(form)
self.assertIn('_users', form.defaults)
self.assertIn('users', form.widgets)
self.assertIsInstance(form.widgets['users'], GridWidget)
def test_make_users_grid(self):
model = self.app.model
view = self.make_view()
person = model.Person(full_name="John Doe")
# basic
grid = view.make_users_grid(person)
self.assertIsInstance(grid, Grid)
self.assertFalse(grid.linked_columns)
self.assertFalse(grid.actions)
# view + edit actions
with patch.object(self.request, 'is_root', new=True):
grid = view.make_users_grid(person)
self.assertIsInstance(grid, Grid)
self.assertIn('username', grid.linked_columns)
self.assertEqual(len(grid.actions), 2)
self.assertEqual(grid.actions[0].key, 'view')
self.assertEqual(grid.actions[1].key, 'edit')
def test_objectify(self):
model = self.app.model
view = self.make_view()
# creating
form = view.make_model_form()
form.validated = {'first_name': 'Barney', 'last_name': 'Rubble'}
person = view.objectify(form)
self.assertEqual(person.full_name, 'Barney Rubble')
# editing
form = view.make_model_form(model_instance=person)
form.validated = {'first_name': 'Betty', 'last_name': 'Rubble'}
person2 = view.objectify(form)
self.assertEqual(person2.full_name, 'Betty Rubble')
self.assertIs(person2, person)
def test_autocomplete_query(self):
model = self.app.model

View file

@ -64,17 +64,51 @@ class TestUserView(WebTestCase):
def test_configure_form(self):
model = self.app.model
barney = model.User(username='barney')
person = model.Person(first_name='Barney', last_name='Rubble', full_name='Barney Rubble')
barney = model.User(username='barney', person=person)
self.session.add(barney)
self.session.commit()
view = self.make_view()
# person is *not* required
with patch.object(view, 'creating', new=True):
form = view.make_form(model_class=model.User)
self.assertIsNone(form.is_required('person'))
# person replaced with first/last name when creating or editing
with patch.object(view, 'viewing', new=True):
form = view.make_form(model_instance=barney)
self.assertIn('person', form)
self.assertNotIn('first_name', form)
self.assertNotIn('last_name', form)
view.configure_form(form)
self.assertFalse(form.is_required('person'))
self.assertIn('person', form)
self.assertNotIn('first_name', form)
self.assertNotIn('last_name', form)
with patch.object(view, 'creating', new=True):
form = view.make_form(model_instance=barney)
self.assertIn('person', form)
self.assertNotIn('first_name', form)
self.assertNotIn('last_name', form)
view.configure_form(form)
self.assertNotIn('person', form)
self.assertIn('first_name', form)
self.assertIn('last_name', form)
with patch.object(view, 'editing', new=True):
form = view.make_form(model_instance=barney)
self.assertIn('person', form)
self.assertNotIn('first_name', form)
self.assertNotIn('last_name', form)
view.configure_form(form)
self.assertNotIn('person', form)
self.assertIn('first_name', form)
self.assertIn('last_name', form)
# first/last name have default values when editing
with patch.object(view, 'editing', new=True):
form = view.make_form(model_instance=barney)
self.assertNotIn('first_name', form.defaults)
self.assertNotIn('last_name', form.defaults)
view.configure_form(form)
self.assertIn('first_name', form.defaults)
self.assertEqual(form.defaults['first_name'], 'Barney')
self.assertIn('last_name', form.defaults)
self.assertEqual(form.defaults['last_name'], 'Rubble')
# password removed (always, for now)
with patch.object(view, 'viewing', new=True):
@ -96,7 +130,7 @@ class TestUserView(WebTestCase):
self.session.add(user)
self.session.commit()
with patch.object(mod, 'Session', return_value=self.session):
with patch.object(view, 'Session', return_value=self.session):
# invalid if same username in data
node = colander.SchemaNode(colander.String(), name='username')
@ -111,6 +145,8 @@ class TestUserView(WebTestCase):
def test_objectify(self):
model = self.app.model
auth = self.app.get_auth_handler()
view = self.make_view()
blokes = model.Role(name="Blokes")
self.session.add(blokes)
others = model.Role(name="Others")
@ -120,30 +156,90 @@ class TestUserView(WebTestCase):
barney.roles.append(blokes)
self.session.add(barney)
self.session.commit()
view = self.make_view()
view.editing = True
self.request.matchdict = {'uuid': barney.uuid}
# sanity check, user is just in 'blokes' role
self.session.refresh(barney)
self.assertEqual(len(barney.roles), 1)
self.assertEqual(barney.roles[0].name, "Blokes")
with patch.object(self.request, 'matchdict', new={'uuid': barney.uuid}):
with patch.object(view, 'editing', new=True):
# form can update user password
self.assertTrue(auth.check_user_password(barney, 'testpass'))
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'set_password': 'testpass2'}
user = view.objectify(form, session=self.session)
self.assertIs(user, barney)
self.assertTrue(auth.check_user_password(barney, 'testpass2'))
# sanity check, user is just in 'blokes' role
self.session.refresh(barney)
self.assertEqual(len(barney.roles), 1)
self.assertEqual(barney.roles[0].name, "Blokes")
# form can update user roles
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'roles': {others.uuid}}
user = view.objectify(form, session=self.session)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 1)
self.assertEqual(user.roles[0].name, "Others")
# form can update user password
self.assertTrue(auth.check_user_password(barney, 'testpass'))
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'set_password': 'testpass2'}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertTrue(auth.check_user_password(barney, 'testpass2'))
# form can update user roles
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'roles': {others.uuid}}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 1)
self.assertEqual(user.roles[0].name, "Others")
# person is auto-created
self.assertIsNone(barney.person)
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'first_name': 'Barney', 'last_name': 'Rubble'}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIsNotNone(barney.person)
self.assertEqual(barney.person.first_name, 'Barney')
self.assertEqual(barney.person.last_name, 'Rubble')
self.assertEqual(barney.person.full_name, 'Barney Rubble')
# person is auto-removed
self.assertIsNotNone(barney.person)
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'first_name': '', 'last_name': ''}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIsNone(barney.person)
# nb. re-attach the person
barney.person = self.session.query(model.Person).one()
# person name is updated
self.assertEqual(barney.person.first_name, 'Barney')
self.assertEqual(barney.person.last_name, 'Rubble')
self.assertEqual(barney.person.full_name, 'Barney Rubble')
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'first_name': 'Fred', 'last_name': 'Flintstone'}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIsNotNone(barney.person)
self.assertEqual(barney.person.first_name, 'Fred')
self.assertEqual(barney.person.last_name, 'Flintstone')
self.assertEqual(barney.person.full_name, 'Fred Flintstone')
with patch.object(view, 'creating', new=True):
# person is auto-created when making new user
form = view.make_model_form()
form.validated = {'username': 'betty', 'first_name': 'Betty', 'last_name': 'Boop'}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIsNotNone(user.person)
self.assertEqual(user.person.first_name, 'Betty')
self.assertEqual(user.person.last_name, 'Boop')
self.assertEqual(user.person.full_name, 'Betty Boop')
# nb. keep ref to last user
last_user = user
# person is *not* auto-created if no name provided
form = view.make_model_form()
form.validated = {'username': 'betty', 'first_name': '', 'last_name': ''}
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIsNone(user.person)
self.assertIsNot(user, last_user)
def test_update_roles(self):
model = self.app.model
@ -166,7 +262,7 @@ class TestUserView(WebTestCase):
# no error if data is missing roles
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barneyx'}
user = view.objectify(form, session=self.session)
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(barney.username, 'barneyx')
@ -182,7 +278,8 @@ class TestUserView(WebTestCase):
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney',
'roles': {admin.uuid, authed.uuid, anon.uuid, others.uuid}}
user = view.objectify(form, session=self.session)
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 1)
self.assertEqual(user.roles[0].name, "Others")
@ -194,7 +291,8 @@ class TestUserView(WebTestCase):
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney',
'roles': {admin.uuid, blokes.uuid, others.uuid}}
user = view.objectify(form, session=self.session)
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 3)
role_uuids = set([role.uuid for role in user.roles])
@ -205,7 +303,8 @@ class TestUserView(WebTestCase):
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney',
'roles': {blokes.uuid, others.uuid}}
user = view.objectify(form, session=self.session)
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 3)
@ -214,6 +313,7 @@ class TestUserView(WebTestCase):
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney',
'roles': {blokes.uuid, others.uuid}}
user = view.objectify(form, session=self.session)
with patch.object(view, 'Session', return_value=self.session):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 2)