diff --git a/src/related/functions.py b/src/related/functions.py index 1f8d489..0d5acc7 100644 --- a/src/related/functions.py +++ b/src/related/functions.py @@ -24,7 +24,7 @@ def to_dict(obj, **kwargs): :param obj: object instance :param kwargs: keyword arguments such as suppress_private_attr, - suppress_empty_values, dict_factory + suppress_empty_values, dict_factory, ignore_keys :return: converted dictionary. """ @@ -77,7 +77,8 @@ def related_obj_to_dict(obj, **kwargs): continue # field name can be overridden by the metadata field - key_name = a.metadata.get('key') or a.name + if kwargs.get('ignore_keys', False): key_name = a.name + else: key_name = a.metadata.get('key') or a.name # store converted / formatted value into return dictionary return_dict[key_name] = value @@ -85,11 +86,12 @@ def related_obj_to_dict(obj, **kwargs): return return_dict -def to_model(cls, value): +def to_model(cls, value, **kwargs): """ Coerce a value into a model object based on a class-type (cls). :param cls: class type to coerce into :param value: value to be coerced + :param kwargs: accepts ignore_keys param to avoid using metadata key field :return: original value or coerced value (value') """ @@ -100,7 +102,7 @@ def to_model(cls, value): value = cls(value) elif is_model(cls) and isinstance(value, dict): - value = convert_key_to_attr_names(cls, value) + value = convert_key_to_attr_names(cls, value, **kwargs) value = cls(**value) else: @@ -109,14 +111,16 @@ def to_model(cls, value): return value -def convert_key_to_attr_names(cls, original): +def convert_key_to_attr_names(cls, original, **kwargs): """ convert key names to their corresponding attribute names """ attrs = fields(cls) updated = {} keys_pulled = set() for a in attrs: - key_name = a.metadata.get('key') or a.name + # field name can be overridden by the metadata field + if kwargs.get('ignore_keys', False): key_name = a.name + else: key_name = a.metadata.get('key') or a.name if key_name in original: updated[a.name] = original.get(key_name) keys_pulled.add(key_name) diff --git a/tests/issues/test_issue_046.py b/tests/issues/test_issue_046.py new file mode 100644 index 0000000..088d106 --- /dev/null +++ b/tests/issues/test_issue_046.py @@ -0,0 +1,40 @@ +import related +import pytest + + +@related.immutable +class Person: + a_name = related.StringField(key='name') + an_age = related.IntegerField(key='age') + + +def test_no_kwarg(): + json = {'name': 'John Doe', 'age': 74} + person = related.to_model(Person, json) + assert json == related.to_dict(person) + + +def test_deserialization_kwarg(): + json_no_kwarg = {'name': 'John Doe', 'age': 74} + json_kwarg = {'a_name': 'John Doe', 'an_age': 74} + person = related.to_model(Person, json_kwarg, ignore_keys=True) + assert json_no_kwarg == related.to_dict(person) + + +def test_serialization_kwarg(): + json_no_kwarg = {'name': 'John Doe', 'age': 74} + json_kwarg = {'a_name': 'John Doe', 'an_age': 74} + person = related.to_model(Person, json_no_kwarg) + assert json_kwarg == related.to_dict(person, ignore_keys=True) + + +def test_invalid_mapping_no_kwarg(): + json_no_kwarg = {'name': 'John Doe', 'age': 74} + with pytest.raises(TypeError): + related.to_model(Person, json_no_kwarg) + + +def test_invalid_mapping_kwarg(): + json_kwarg = {'a_name': 'John Doe', 'an_age': 74} + with pytest.raises(TypeError): + related.to_model(Person, json_kwarg, ignore_keys=True)