Skip to content

Commit

Permalink
Merge 37b4115 into 3e765b3
Browse files Browse the repository at this point in the history
  • Loading branch information
lafrech committed Mar 6, 2019
2 parents 3e765b3 + 37b4115 commit f84e891
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 18 deletions.
9 changes: 8 additions & 1 deletion HISTORY.rst
Expand Up @@ -7,7 +7,14 @@ dev
* ``missing`` attribute is no longer used in umongo fields, only ``default`` is
used. ``marshmallow_missing`` and ``marshmallow_default`` attribute can be
used to overwrite the value to use in the pure marshmallow field returned
by `as_marshmallow_field` method.
by ``as_marshmallow_field`` method.
* ``as_marshmallow_field`` does not pass ``load_from``, ``dump_to`` and
``attribute`` to the pure marshmallow field anymore. It only passes
``validate``, ``required``, ``allow_none``, ``dump_only``, ``load_only`` and
``error_messages``, as well as ``default`` and ``missing`` values inferred
from umongo's ``default``. Parameters prefixed with ``marshmallow_`` in the
umongo field are passed to the pure marshmallow field and override their
non-prefixed counterpart.

1.2.0 (2019-02-08)
------------------
Expand Down
53 changes: 53 additions & 0 deletions tests/test_marshmallow.py
Expand Up @@ -133,6 +133,59 @@ class Meta:
assert ma_custom_base_schema.Meta.exclude == ('content',)
assert ma_custom_base_schema.Meta.dump_only == ('id',)

def test_as_marshmallow_field_pass_params(self):
@self.instance.register
class MyDoc(Document):
lf = fields.IntField(marshmallow_load_from='lflf')
dt = fields.IntField(marshmallow_dump_to='dtdt')
at = fields.IntField(marshmallow_attribute='atat')
re = fields.IntField(marshmallow_required=True)
an = fields.IntField(marshmallow_allow_none=True)
lo = fields.IntField(marshmallow_load_only=True)
do = fields.IntField(marshmallow_dump_only=True)
va = fields.IntField(marshmallow_validate=validate.Range(min=0))
em = fields.IntField(marshmallow_error_messages={'invalid': 'Wrong'})

MyMaDoc = MyDoc.schema.as_marshmallow_schema()

assert MyMaDoc().fields['lf'].load_from == 'lflf'
assert MyMaDoc().fields['dt'].dump_to == 'dtdt'
assert MyMaDoc().fields['at'].attribute == 'atat'
assert MyMaDoc().fields['re'].required is True
assert MyMaDoc().fields['an'].allow_none is True
assert MyMaDoc().fields['lo'].load_only is True
assert MyMaDoc().fields['do'].dump_only is True
_, err = MyMaDoc().load({'va': -1})
assert 'va' in err
assert MyMaDoc().fields['em'].error_messages['invalid'] == 'Wrong'

def test_as_marshmallow_field_infer_missing_default(self):
@self.instance.register
class MyDoc(Document):
de = fields.IntField(default=42)
mm = fields.IntField(marshmallow_missing=12)
md = fields.IntField(marshmallow_default=12)
mmd = fields.IntField(default=42, marshmallow_missing=12)
mdd = fields.IntField(default=42, marshmallow_default=12)

MyMaDoc = MyDoc.schema.as_marshmallow_schema()

data, _ = MyMaDoc().load({})
assert data == {
'de': 42,
'mm': 12,
'mmd': 12,
'mdd': 42,
}

data, _ = MyMaDoc().dump({})
assert data == {
'de': 42,
'md': 12,
'mmd': 42,
'mdd': 12,
}

def test_as_marshmallow_schema_cache(self):
ma_schema_cls = self.User.schema.as_marshmallow_schema()

Expand Down
57 changes: 40 additions & 17 deletions umongo/abstract.py
Expand Up @@ -117,7 +117,29 @@ def __init__(self, *args, io_validate=None, unique=False, instance=None, **kwarg
"generating pure Marshmallow field.")
if 'default' in kwargs:
kwargs['missing'] = kwargs['default']

# Store attributes prefixed with marshmallow_ to use them when
# creating pure marshmallow Schema
for attribute in (
'load_from', 'dump_to', 'attribute',
'validate', 'required', 'allow_none',
'load_only', 'dump_only', 'error_messages'
):
attribute = 'marshmallow_' + attribute
if attribute in kwargs:
setattr(self, attribute, kwargs.pop(attribute))

# Infer from "default" parameter a default value for
# marshmallow_default and marshmallow_missing
def serialize_default():
val = self.default() if callable(self.default) else self.default
return self.serialize('foo', {'foo': val})

self.marshmallow_missing = kwargs.pop('marshmallow_missing', serialize_default)
self.marshmallow_default = kwargs.pop('marshmallow_default', serialize_default)

super().__init__(*args, **kwargs)

# Overwrite error_messages to handle i18n translation
self.error_messages = I18nErrorDict(self.error_messages)
# `io_validate` will be run after `io_validate_resursive`
Expand All @@ -128,13 +150,6 @@ def __init__(self, *args, io_validate=None, unique=False, instance=None, **kwarg
self.unique = unique
self.instance = instance

def serialize_default():
val = self.default() if callable(self.default) else self.default
return self.serialize('foo', {'foo': val})

self.marshmallow_missing = kwargs.get('marshmallow_missing', serialize_default)
self.marshmallow_default = kwargs.get('marshmallow_default', serialize_default)

def __repr__(self):
return ('<fields.{ClassName}(default={self.default!r}, '
'attribute={self.attribute!r}, '
Expand Down Expand Up @@ -213,18 +228,26 @@ def translate_query(self, key, query):
return {self.attribute or key: query}

def _extract_marshmallow_field_params(self, mongo_world):
params = {field: getattr(self, field)
for field in ('load_from', 'validate', 'required',
'allow_none', 'load_only',
'dump_only', 'error_messages')}
params = {
attribute: getattr(self, attribute)
for attribute in (
'validate', 'required', 'allow_none',
'dump_only', 'load_only', 'error_messages'
)
}
if mongo_world and self.attribute:
params['attribute'] = self.attribute
# Marshmallow doesn't serialize default value when doing `dump`,
# hence we use the serialized value..
params['default'] = self.marshmallow_default
# ...on the other hand, marshmallow does use unserialize on the
# missing value when doing `load`, hence also use the serialized value
params['missing'] = self.marshmallow_missing

# Override uMongo attributes with marshmallow_ prefixed attributes
for attribute in (
'default', 'missing', 'load_from', 'dump_to', 'attribute',
'validate', 'required', 'allow_none',
'load_only', 'dump_only', 'error_messages'
):
ma_attribute = 'marshmallow_' + attribute
if hasattr(self, ma_attribute):
params[attribute] = getattr(self, ma_attribute)

params.update(self.metadata)
return params

Expand Down

0 comments on commit f84e891

Please sign in to comment.