使用 Marshmallow 反序列化强制执行严格的 fields.Date 格式
Enforce strict fields.Date format with Marshmallow deserialization
我正在为 Flask 项目使用 Marshmallow 2.15.3,并希望强制执行严格的日期和日期时间格式。严格的意思是我只想接受与以下格式相同的字符串。我遇到的是 Date 和 DateTime 的处理上的一些差异。格式:
DATE_FORMAT = '%Y-%m-%d'
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
例如,使用 DateTime 我可以执行以下操作:
dt = fields.DateTime(format=DATETIME_FORMAT)
dt.deserialize('2018-01-01') # fails, as desired
dt.deserialize('2018-01-01T05:06:08.012312+02:00') # fails, as desired
dt.deserialize('2018-01-01T05:06:08') # works, as desired and according to format
有了 Date,我可以执行以下操作:
d = fields.Date() # does not accept format argument
d.deserialize('2018-01') # fails, as desired
d.deserialize('2018-01-01T05:06:08.012312+02:00') # works, NOT as desired
d.deserialize('2018-01-01') # works, as desired and according to format
虽然 DateTime 不允许额外信息,但 Date 允许。据我了解,日期字段没有 format
参数。有什么办法可以解决这个问题以获得类似的功能,并针对太短和太长的输入值强制执行我的严格格式?
对于面向未来的代码,我发现 Marshmallow 3.0.0b17 中的 Date
class 现在是 DateTime
的子 class 而不是 Field
, 使其继承 format
kwarg (relevant commit).
对于版本 2.15.3 和(2.X.X 一般而言)我找不到任何内置函数。解决方法是猴子修补 fields.Date
class。修改后看起来像这样:
class Date(Field):
"""ISO8601-formatted date string.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
default_error_messages = {
'invalid': 'Not a valid date.',
'format': '"{input}" cannot be formatted as a date.',
}
def __init__(self, format=None, **kwargs):
super(Date, self).__init__(**kwargs)
self.dateformat = format
def _serialize(self, value, attr, obj):
if value is None:
return None
try:
return value.isoformat()
except AttributeError:
self.fail('format', input=value)
return value
def _deserialize(self, value, attr, data):
"""Deserialize an ISO8601-formatted date string to a
:class:`datetime.date` object.
"""
if not value: # falsy values are invalid
self.fail('invalid')
elif self.dateformat:
try:
return dt.datetime.strptime(value, self.dateformat).date()
except (TypeError, AttributeError, ValueError):
raise self.fail('invalid')
try:
return utils.from_iso_date(value)
except (AttributeError, TypeError, ValueError):
self.fail('invalid')
此处的修改是添加了 __init__
定义,在 _deserialize
下添加了整个 elif self.dateformat
子句。这允许我使用提交的格式反序列化,例如:
d = fields.Date('%Y-%m-%d') # now accepts a format
d.deserialize('2018-01-01T05:06:08.012312+02:00') # fails, as desired
我正在为 Flask 项目使用 Marshmallow 2.15.3,并希望强制执行严格的日期和日期时间格式。严格的意思是我只想接受与以下格式相同的字符串。我遇到的是 Date 和 DateTime 的处理上的一些差异。格式:
DATE_FORMAT = '%Y-%m-%d'
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
例如,使用 DateTime 我可以执行以下操作:
dt = fields.DateTime(format=DATETIME_FORMAT)
dt.deserialize('2018-01-01') # fails, as desired
dt.deserialize('2018-01-01T05:06:08.012312+02:00') # fails, as desired
dt.deserialize('2018-01-01T05:06:08') # works, as desired and according to format
有了 Date,我可以执行以下操作:
d = fields.Date() # does not accept format argument
d.deserialize('2018-01') # fails, as desired
d.deserialize('2018-01-01T05:06:08.012312+02:00') # works, NOT as desired
d.deserialize('2018-01-01') # works, as desired and according to format
虽然 DateTime 不允许额外信息,但 Date 允许。据我了解,日期字段没有 format
参数。有什么办法可以解决这个问题以获得类似的功能,并针对太短和太长的输入值强制执行我的严格格式?
对于面向未来的代码,我发现 Marshmallow 3.0.0b17 中的 Date
class 现在是 DateTime
的子 class 而不是 Field
, 使其继承 format
kwarg (relevant commit).
对于版本 2.15.3 和(2.X.X 一般而言)我找不到任何内置函数。解决方法是猴子修补 fields.Date
class。修改后看起来像这样:
class Date(Field):
"""ISO8601-formatted date string.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
default_error_messages = {
'invalid': 'Not a valid date.',
'format': '"{input}" cannot be formatted as a date.',
}
def __init__(self, format=None, **kwargs):
super(Date, self).__init__(**kwargs)
self.dateformat = format
def _serialize(self, value, attr, obj):
if value is None:
return None
try:
return value.isoformat()
except AttributeError:
self.fail('format', input=value)
return value
def _deserialize(self, value, attr, data):
"""Deserialize an ISO8601-formatted date string to a
:class:`datetime.date` object.
"""
if not value: # falsy values are invalid
self.fail('invalid')
elif self.dateformat:
try:
return dt.datetime.strptime(value, self.dateformat).date()
except (TypeError, AttributeError, ValueError):
raise self.fail('invalid')
try:
return utils.from_iso_date(value)
except (AttributeError, TypeError, ValueError):
self.fail('invalid')
此处的修改是添加了 __init__
定义,在 _deserialize
下添加了整个 elif self.dateformat
子句。这允许我使用提交的格式反序列化,例如:
d = fields.Date('%Y-%m-%d') # now accepts a format
d.deserialize('2018-01-01T05:06:08.012312+02:00') # fails, as desired