fix: prevent error in DateTime schema type if no widget/request set
when we use this intentionally, the widget/request should be set as expected. but apparently this gets instantiated sometimes (by ColanderAlchemy?) without a widget. so this adds sane fallback logic, instead of outright error
This commit is contained in:
parent
131eb22580
commit
2ccfe29553
2 changed files with 28 additions and 9 deletions
|
|
@ -31,6 +31,7 @@ import colander
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from wuttjamaican.conf import parse_list
|
from wuttjamaican.conf import parse_list
|
||||||
|
from wuttjamaican.util import localtime
|
||||||
|
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
from wuttaweb.forms import widgets
|
from wuttaweb.forms import widgets
|
||||||
|
|
@ -38,28 +39,38 @@ from wuttaweb.forms import widgets
|
||||||
|
|
||||||
class WuttaDateTime(colander.DateTime):
|
class WuttaDateTime(colander.DateTime):
|
||||||
"""
|
"""
|
||||||
Custom schema type for ``datetime`` fields.
|
Custom schema type for :class:`~python:datetime.datetime` fields.
|
||||||
|
|
||||||
This should be used automatically for
|
This should be used automatically for
|
||||||
:class:`sqlalchemy:sqlalchemy.types.DateTime` columns unless you
|
:class:`~sqlalchemy:sqlalchemy.types.DateTime` ORM columns unless
|
||||||
register another default.
|
you register another default.
|
||||||
|
|
||||||
This schema type exists for sake of convenience, when working with
|
This schema type exists for sake of convenience, when working with
|
||||||
the Buefy datepicker + timepicker widgets.
|
the Buefy datepicker + timepicker widgets.
|
||||||
|
|
||||||
|
It also follows the datetime handling "rules" as outlined in
|
||||||
|
:doc:`wuttjamaican:narr/datetime`. On the Python side, values
|
||||||
|
should be naive/UTC datetime objects. On the HTTP side, values
|
||||||
|
will be ISO-format strings representing aware/local time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def serialize(self, node, appstruct):
|
def serialize(self, node, appstruct):
|
||||||
if not appstruct:
|
if not appstruct:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
|
||||||
|
# nb. request should be present when it matters
|
||||||
|
if node.widget and node.widget.request:
|
||||||
request = node.widget.request
|
request = node.widget.request
|
||||||
config = request.wutta_config
|
config = request.wutta_config
|
||||||
app = config.get_app()
|
app = config.get_app()
|
||||||
|
appstruct = app.localtime(appstruct)
|
||||||
|
else:
|
||||||
|
# but if not, fallback to config-less logic
|
||||||
|
appstruct = localtime(appstruct)
|
||||||
|
|
||||||
dt = app.localtime(appstruct)
|
|
||||||
if self.format:
|
if self.format:
|
||||||
return dt.strftime(self.format)
|
return appstruct.strftime(self.format)
|
||||||
return dt.isoformat()
|
return appstruct.isoformat()
|
||||||
|
|
||||||
def deserialize( # pylint: disable=inconsistent-return-statements
|
def deserialize( # pylint: disable=inconsistent-return-statements
|
||||||
self, node, cstruct
|
self, node, cstruct
|
||||||
|
|
@ -72,6 +83,7 @@ class WuttaDateTime(colander.DateTime):
|
||||||
"%Y-%m-%dT%I:%M %p",
|
"%Y-%m-%dT%I:%M %p",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# nb. request is always assumed to be present here
|
||||||
request = node.widget.request
|
request = node.widget.request
|
||||||
config = request.wutta_config
|
config = request.wutta_config
|
||||||
app = config.get_app()
|
app = config.get_app()
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,13 @@ class TestWuttaDateTime(WebTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(result, "2024-12-11 02:33 PM")
|
self.assertEqual(result, "2024-12-11 02:33 PM")
|
||||||
|
|
||||||
|
# missing widget/request/config
|
||||||
|
typ = mod.WuttaDateTime()
|
||||||
|
node = colander.SchemaNode(typ)
|
||||||
|
result = typ.serialize(node, datetime.datetime(2024, 12, 11, 22, 33))
|
||||||
|
# nb. not possible to know which timezone is system-local
|
||||||
|
self.assertTrue(result.startswith("2024-12-"))
|
||||||
|
|
||||||
def test_deserialize(self):
|
def test_deserialize(self):
|
||||||
tzlocal = get_timezone_by_name("America/Los_Angeles")
|
tzlocal = get_timezone_by_name("America/Los_Angeles")
|
||||||
with patch.object(self.app, "get_timezone", return_value=tzlocal):
|
with patch.object(self.app, "get_timezone", return_value=tzlocal):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue