diff --git a/src/wuttaweb/app.py b/src/wuttaweb/app.py
index 88318b4..845b41f 100644
--- a/src/wuttaweb/app.py
+++ b/src/wuttaweb/app.py
@@ -61,7 +61,7 @@ class WebAppProvider(AppProvider):
return self.web_handler
-def make_wutta_config(settings, config_maker=None, **kwargs):
+def make_wutta_config(settings):
"""
Make a WuttaConfig object from the given settings.
@@ -93,9 +93,8 @@ def make_wutta_config(settings, config_maker=None, **kwargs):
"section of config to the path of your "
"config file. Lame, but necessary.")
- # make config, add to settings
- config_maker = config_maker or make_config
- wutta_config = config_maker(path, **kwargs)
+ # make config per usual, add to settings
+ wutta_config = make_config(path)
settings['wutta_config'] = wutta_config
# configure database sessions
diff --git a/src/wuttaweb/forms/base.py b/src/wuttaweb/forms/base.py
index ceaeeb7..c3567b5 100644
--- a/src/wuttaweb/forms/base.py
+++ b/src/wuttaweb/forms/base.py
@@ -746,39 +746,17 @@ class Form:
kwargs = {}
if self.model_instance:
-
- # TODO: i keep finding problems with this, not sure
- # what needs to happen. some forms will have a simple
- # dict for model_instance, others will have a proper
- # SQLAlchemy object. and in the latter case, it may
- # not be "wutta-native" but from another DB.
-
- # so the problem is, how to detect whether we should
- # use the model_instance as-is or if we should convert
- # to a dict. some options include:
-
- # - check if instance has dictify() method
- # i *think* this was tried and didn't work? but do not recall
-
- # - check if is instance of model.Base
- # this is unreliable since model.Base is wutta-native
-
- # - check if form has a model_class
- # has not been tried yet
-
- # - check if schema is from colanderalchemy
- # this is what we are trying currently...
-
- if isinstance(schema, SQLAlchemySchemaNode):
+ # TODO: would it be smarter to test with hasattr() ?
+ # if hasattr(schema, 'dictify'):
+ if isinstance(self.model_instance, model.Base):
kwargs['appstruct'] = schema.dictify(self.model_instance)
else:
kwargs['appstruct'] = self.model_instance
- # create the Deform instance
+ form = deform.Form(schema, **kwargs)
# nb. must give a reference back to wutta form; this is
# for sake of field schema nodes and widgets, e.g. to
# access the main model instance
- form = deform.Form(schema, **kwargs)
form.wutta_form = self
self.deform_form = form
@@ -944,13 +922,18 @@ class Form:
if field_type:
attrs['type'] = field_type
if messages:
- cls = 'is-size-7'
- if field_type == 'is-danger':
- cls += ' has-text-danger'
- messages = [HTML.tag('p', c=[msg], class_=cls)
- for msg in messages]
- slot = HTML.tag('slot', name='messages', c=messages)
- html = HTML.tag('div', c=[html, slot])
+ if len(messages) == 1:
+ msg = messages[0]
+ if msg.startswith('`') and msg.endswith('`'):
+ attrs[':message'] = msg
+ else:
+ attrs['message'] = msg
+ # TODO
+ # else:
+ # # nb. must pass an array as JSON string
+ # attrs[':message'] = '[{}]'.format(', '.join([
+ # "'{}'".format(msg.replace("'", r"\'"))
+ # for msg in messages]))
return HTML.tag('b-field', c=[html], **attrs)
@@ -995,16 +978,7 @@ class Form:
model_data = {}
def assign(field):
- value = field.cstruct
-
- # TODO: we need a proper true/false on the Vue side,
- # but deform/colander want 'true' and 'false' ..so
- # for now we explicitly translate here, ugh. also
- # note this does not yet allow for null values.. :(
- if isinstance(field.typ, colander.Boolean):
- value = True if field.typ.true_val else False
-
- model_data[field.oid] = make_json_safe(value)
+ model_data[field.oid] = make_json_safe(field.cstruct)
for key in self.fields:
@@ -1102,7 +1076,7 @@ class Form:
"""
dform = self.get_deform()
if field in dform:
- field = dform[field]
- if field.error:
- return field.error.messages()
+ error = dform[field].errormsg
+ if error:
+ return [error]
return []
diff --git a/src/wuttaweb/forms/schema.py b/src/wuttaweb/forms/schema.py
index 4402fde..a3a464b 100644
--- a/src/wuttaweb/forms/schema.py
+++ b/src/wuttaweb/forms/schema.py
@@ -258,12 +258,7 @@ class PersonRef(ObjectRef):
This is a subclass of :class:`ObjectRef`.
"""
-
- @property
- def model_class(self):
- """ """
- model = self.app.model
- return model.Person
+ model_class = Person
def sort_query(self, query):
""" """
diff --git a/src/wuttaweb/forms/widgets.py b/src/wuttaweb/forms/widgets.py
index 837b6f1..ee58a1a 100644
--- a/src/wuttaweb/forms/widgets.py
+++ b/src/wuttaweb/forms/widgets.py
@@ -33,17 +33,14 @@ in the namespace:
* :class:`deform:deform.widget.TextAreaWidget`
* :class:`deform:deform.widget.PasswordWidget`
* :class:`deform:deform.widget.CheckedPasswordWidget`
-* :class:`deform:deform.widget.CheckboxWidget`
* :class:`deform:deform.widget.SelectWidget`
* :class:`deform:deform.widget.CheckboxChoiceWidget`
-* :class:`deform:deform.widget.MoneyInputWidget`
"""
import colander
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
PasswordWidget, CheckedPasswordWidget,
- CheckboxWidget, SelectWidget, CheckboxChoiceWidget,
- MoneyInputWidget)
+ SelectWidget, CheckboxChoiceWidget)
from webhelpers2.html import HTML
from wuttaweb.db import Session
@@ -223,7 +220,7 @@ class UserRefsWidget(WuttaCheckboxChoiceWidget):
users = []
if cstruct:
for uuid in cstruct:
- user = self.session.get(model.User, uuid)
+ user = self.session.query(model.User).get(uuid)
if user:
users.append(dict([(key, getattr(user, key))
for key in columns + ['uuid']]))
diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py
index 0f2c812..aa2e413 100644
--- a/src/wuttaweb/grids/base.py
+++ b/src/wuttaweb/grids/base.py
@@ -1047,12 +1047,6 @@ class Grid:
filters = filters or {}
if self.model_class:
- # TODO: i tried using self.get_model_columns() here but in
- # many cases that will be too aggressive. however it is
- # often the case that the *grid* columns are a subset of
- # the unerlying *table* columns. so until a better way
- # is found, we choose "too few" instead of "too many"
- # filters here. surely must improve it at some point.
for key in self.columns:
if key in filters:
continue
diff --git a/src/wuttaweb/templates/deform/checkbox.pt b/src/wuttaweb/templates/deform/checkbox.pt
index c4536e8..92c9f62 100644
--- a/src/wuttaweb/templates/deform/checkbox.pt
+++ b/src/wuttaweb/templates/deform/checkbox.pt
@@ -6,6 +6,6 @@
v-model="${vmodel}"
native-value="true"
tal:attributes="attributes|field.widget.attributes|{};">
- {{ ${vmodel} ? "Yes" : "No" }}
+ {{ ${vmodel} }}
diff --git a/src/wuttaweb/templates/deform/moneyinput.pt b/src/wuttaweb/templates/deform/moneyinput.pt
deleted file mode 100644
index 532a823..0000000
--- a/src/wuttaweb/templates/deform/moneyinput.pt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/src/wuttaweb/templates/deform/readonly/checkbox.pt b/src/wuttaweb/templates/deform/readonly/checkbox.pt
deleted file mode 100644
index 4988213..0000000
--- a/src/wuttaweb/templates/deform/readonly/checkbox.pt
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Yes
- No
-
diff --git a/src/wuttaweb/templates/grids/vue_template.mako b/src/wuttaweb/templates/grids/vue_template.mako
index 1ed408e..edcdc24 100644
--- a/src/wuttaweb/templates/grids/vue_template.mako
+++ b/src/wuttaweb/templates/grids/vue_template.mako
@@ -19,7 +19,7 @@
-
+
`` tag is also given a ``title``
- attribute with the original (full) text, so that appears on
- mouse hover.
-
- To use this feature for your grid::
-
- grid.set_renderer('my_notes_field', self.grid_render_notes)
-
- # you can also override maxlen
- grid.set_renderer('my_notes_field', self.grid_render_notes, maxlen=50)
- """
- if value is None:
- return
-
- if len(value) < maxlen:
- return value
-
- return HTML.tag('span', title=value, c=f"{value[:maxlen]}...")
-
##############################
# support methods
##############################
@@ -1395,8 +1317,9 @@ class MasterView(View):
Default logic for this method returns a "plain" query on the
:attr:`model_class` if that is defined; otherwise ``None``.
"""
+ model = self.app.model
model_class = self.get_model_class()
- if model_class:
+ if model_class and issubclass(model_class, model.Base):
session = session or self.Session()
return session.query(model_class)
@@ -1421,6 +1344,33 @@ class MasterView(View):
# for key in self.get_model_key():
# grid.set_link(key)
+ def grid_render_notes(self, record, key, value, maxlen=100):
+ """
+ Custom grid renderer callable for "notes" fields.
+
+ If the given text ``value`` is shorter than ``maxlen``
+ characters, it is returned as-is.
+
+ But if it is longer, then it is truncated and an ellispsis is
+ added. The resulting ```` tag is also given a ``title``
+ attribute with the original (full) text, so that appears on
+ mouse hover.
+
+ To use this feature for your grid::
+
+ grid.set_renderer('my_notes_field', self.grid_render_notes)
+
+ # you can also override maxlen
+ grid.set_renderer('my_notes_field', self.grid_render_notes, maxlen=50)
+ """
+ if value is None:
+ return
+
+ if len(value) < maxlen:
+ return value
+
+ return HTML.tag('span', title=value, c=f"{value[:maxlen]}...")
+
def get_instance(self, session=None):
"""
This should return the "current" model instance based on the
diff --git a/src/wuttaweb/views/people.py b/src/wuttaweb/views/people.py
index 78cf931..a19df57 100644
--- a/src/wuttaweb/views/people.py
+++ b/src/wuttaweb/views/people.py
@@ -123,12 +123,6 @@ class PersonView(MasterView):
@classmethod
def defaults(cls, config):
""" """
-
- # nb. Person may come from custom model
- wutta_config = config.registry.settings['wutta_config']
- app = wutta_config.get_app()
- cls.model_class = app.model.Person
-
cls._defaults(config)
cls._people_defaults(config)
diff --git a/src/wuttaweb/views/settings.py b/src/wuttaweb/views/settings.py
index aa28416..a20e1f6 100644
--- a/src/wuttaweb/views/settings.py
+++ b/src/wuttaweb/views/settings.py
@@ -51,7 +51,6 @@ class AppInfoView(MasterView):
model_name = 'AppInfo'
model_title_plural = "App Info"
route_prefix = 'appinfo'
- filterable = False
sort_on_backend = False
sort_defaults = 'name'
paginated = False
diff --git a/src/wuttaweb/views/users.py b/src/wuttaweb/views/users.py
index 5e28a26..91ed2e0 100644
--- a/src/wuttaweb/views/users.py
+++ b/src/wuttaweb/views/users.py
@@ -207,17 +207,6 @@ class UserView(MasterView):
role = session.get(model.Role, uuid)
user.roles.remove(role)
- @classmethod
- def defaults(cls, config):
- """ """
-
- # nb. User may come from custom model
- wutta_config = config.registry.settings['wutta_config']
- app = wutta_config.get_app()
- cls.model_class = app.model.User
-
- cls._defaults(config)
-
def defaults(config, **kwargs):
base = globals()
diff --git a/tests/forms/test_base.py b/tests/forms/test_base.py
index 73e9d85..70cb51f 100644
--- a/tests/forms/test_base.py
+++ b/tests/forms/test_base.py
@@ -461,10 +461,15 @@ class TestForm(TestCase):
# nb. no error message
self.assertNotIn('message', html)
- # with error message
- with patch.object(form, 'get_field_errors', return_value=['something is wrong']):
- html = form.render_vue_field('foo')
- self.assertIn('something is wrong', html)
+ # with single "static" error
+ dform['foo'].error = MagicMock(msg="something is wrong")
+ html = form.render_vue_field('foo')
+ self.assertIn(' message="something is wrong"', html)
+
+ # with single "dynamic" error
+ dform['foo'].error = MagicMock(msg="`something is wrong`")
+ html = form.render_vue_field('foo')
+ self.assertIn(':message="`something is wrong`"', html)
# add another field, but not to deform, so it should still
# display but with no widget
@@ -520,33 +525,20 @@ class TestForm(TestCase):
data = form.get_vue_model_data()
self.assertEqual(len(data), 2)
- # confirm bool values make it thru as-is
- schema.add(colander.SchemaNode(colander.Bool(), name='baz'))
- form = self.make_form(schema=schema, model_instance={
- 'foo': 'one',
- 'bar': 'two',
- 'baz': True,
- })
- data = form.get_vue_model_data()
- self.assertEqual(list(data.values()), ['one', 'two', True])
-
def test_get_field_errors(self):
schema = self.make_schema()
-
- # simple 'Required' validation failure
form = self.make_form(schema=schema)
- self.request.method = 'POST'
- self.request.POST = {'foo': 'one'}
- self.assertFalse(form.validate())
- errors = form.get_field_errors('bar')
- self.assertEqual(errors, ['Required'])
+ dform = form.get_deform()
- # no errors
- form = self.make_form(schema=schema)
- self.request.POST = {'foo': 'one', 'bar': 'two'}
- self.assertTrue(form.validate())
- errors = form.get_field_errors('bar')
- self.assertEqual(errors, [])
+ # no error
+ errors = form.get_field_errors('foo')
+ self.assertEqual(len(errors), 0)
+
+ # simple error
+ dform['foo'].error = MagicMock(msg="something is wrong")
+ errors = form.get_field_errors('foo')
+ self.assertEqual(len(errors), 1)
+ self.assertEqual(errors[0], "something is wrong")
def test_validate(self):
schema = self.make_schema()
diff --git a/tests/views/test_master.py b/tests/views/test_master.py
index 023449a..d000693 100644
--- a/tests/views/test_master.py
+++ b/tests/views/test_master.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8; -*-
-import decimal
import functools
from unittest import TestCase
from unittest.mock import MagicMock, patch
@@ -559,44 +558,6 @@ class TestMasterView(WebTestCase):
view.configure_grid(grid)
self.assertNotIn('uuid', grid.columns)
- def test_grid_render_bool(self):
- model = self.app.model
- view = self.make_view()
- user = model.User(username='barney', active=None)
-
- # null
- value = view.grid_render_bool(user, 'active', None)
- self.assertIsNone(value)
-
- # true
- user.active = True
- value = view.grid_render_bool(user, 'active', True)
- self.assertEqual(value, "Yes")
-
- # false
- user.active = False
- value = view.grid_render_bool(user, 'active', False)
- self.assertEqual(value, "No")
-
- def test_grid_render_currency(self):
- model = self.app.model
- view = self.make_view()
- obj = {'amount': None}
-
- # null
- value = view.grid_render_currency(obj, 'amount', None)
- self.assertIsNone(value)
-
- # normal amount
- obj['amount'] = decimal.Decimal('100.42')
- value = view.grid_render_currency(obj, 'amount', '100.42')
- self.assertEqual(value, "$100.42")
-
- # negative amount
- obj['amount'] = decimal.Decimal('-100.42')
- value = view.grid_render_currency(obj, 'amount', '-100.42')
- self.assertEqual(value, "($100.42)")
-
def test_grid_render_notes(self):
model = self.app.model
view = self.make_view()