From ce741ab2129bde469942c002246e9240b4b38920 Mon Sep 17 00:00:00 2001 From: Justin Phillips Date: Mon, 7 Nov 2016 17:17:50 -0800 Subject: [PATCH] deserialize map attributes correctly (#192) --- pynamodb/models.py | 2 +- pynamodb/tests/data.py | 47 ++++++++++++++++++++++++++++++++++++ pynamodb/tests/test_model.py | 36 +++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/pynamodb/models.py b/pynamodb/models.py index 8489ad5f3..261aec1c7 100644 --- a/pynamodb/models.py +++ b/pynamodb/models.py @@ -438,7 +438,7 @@ def from_raw_data(cls, data): attr = cls._get_attributes().get(name, None) if attr: deserialized_attr = attr.deserialize(attr.get_value(value)) - if isinstance(attr, MapAttribute): + if isinstance(attr, MapAttribute) and not type(attr) == MapAttribute: deserialized_attr = type(attr)(**deserialized_attr) kwargs[name] = deserialized_attr return cls(*args, **kwargs) diff --git a/pynamodb/tests/data.py b/pynamodb/tests/data.py index e058ee276..6273047f4 100644 --- a/pynamodb/tests/data.py +++ b/pynamodb/tests/data.py @@ -1192,3 +1192,50 @@ } } } + +EXPLICIT_RAW_MAP_MODEL_TABLE_DATA = { + 'Table': { + 'ItemCount': 0, 'TableName': 'ExplicitRawMapModel', + 'ProvisionedThroughput': { + 'ReadCapacityUnits': 2, + 'WriteCapacityUnits': 2, + 'NumberOfDecreasesToday': 0 + }, + 'CreationDateTime': 1391471876.86, + 'TableStatus': 'ACTIVE', + 'AttributeDefinitions': [ + { + 'AttributeName': 'map_id', + 'AttributeType': 'N' + }, + { + 'AttributeName': 'map_attr', + 'AttributeType': 'M' + } + ], + 'KeySchema': [ + { + 'AttributeName': 'map_id', 'KeyType': 'HASH' + } + ], + 'TableSizeBytes': 0 + } +} + +EXPLICIT_RAW_MAP_MODEL_ITEM_DATA = { + 'Item': { + 'map_id': { + 'N': '123', + }, + 'map_attr': {'M': { + 'foo': {'S': 'bar'}, + 'num': {'N': '1'}, + 'bool_type': {'BOOL': True}, + 'other_b_type': {'BOOL': False}, + 'floaty': {'N': '1.2'}, + 'listy': {'L': [{'N': '1'}, {'N': '2'}, {'N': '3'}]}, + 'mapy': {'M': {'baz': {'S': 'bongo'}}} + } + } + } +} diff --git a/pynamodb/tests/test_model.py b/pynamodb/tests/test_model.py index b0f93be15..ea21a42ba 100644 --- a/pynamodb/tests/test_model.py +++ b/pynamodb/tests/test_model.py @@ -43,7 +43,8 @@ BOOLEAN_CONVERSION_MODEL_TABLE_DATA, BOOLEAN_CONVERSION_MODEL_NEW_STYLE_FALSE_ITEM_DATA, BOOLEAN_CONVERSION_MODEL_NEW_STYLE_TRUE_ITEM_DATA, BOOLEAN_CONVERSION_MODEL_OLD_STYLE_FALSE_ITEM_DATA, BOOLEAN_CONVERSION_MODEL_OLD_STYLE_TRUE_ITEM_DATA, - BOOLEAN_CONVERSION_MODEL_TABLE_DATA_OLD_STYLE, TREE_MODEL_TABLE_DATA, TREE_MODEL_ITEM_DATA + BOOLEAN_CONVERSION_MODEL_TABLE_DATA_OLD_STYLE, TREE_MODEL_TABLE_DATA, TREE_MODEL_ITEM_DATA, + EXPLICIT_RAW_MAP_MODEL_TABLE_DATA, EXPLICIT_RAW_MAP_MODEL_ITEM_DATA ) if six.PY3: @@ -400,7 +401,7 @@ class Meta: class ExplicitRawMapModel(Model): class Meta: table_name = 'ExplicitRawMapModel' - + map_id = NumberAttribute(hash_key=True, default=123) map_attr = MapAttribute() @@ -3076,3 +3077,34 @@ def test_raw_map_deserializes(self): for k,v in six.iteritems(map_native): self.assertEqual(v, actual[k]) + def test_raw_map_from_raw_data_works(self): + map_native = { + 'foo': 'bar', 'num': 1, 'bool_type': True, + 'other_b_type': False, 'floaty': 1.2, 'listy': [1, 2, 3], + 'mapy': {'baz': 'bongo'} + } + map_serialized = { + + 'M': { + 'foo': {'S': 'bar'}, + 'num': {'N': 1}, + 'bool_type': {'BOOL': True}, + 'other_b_type': {'BOOL': False}, + 'floaty': {'N': 1.2}, + 'listy': {'L': [{'N': 1}, {'N': 2}, {'N': 3}]}, + 'mapy': {'M': {'baz': {'S': 'bongo'}}} + } + } + instance = ExplicitRawMapModel(map_attr=map_native, map_id=123) + fake_db = self.database_mocker(ExplicitRawMapModel, + EXPLICIT_RAW_MAP_MODEL_TABLE_DATA, + EXPLICIT_RAW_MAP_MODEL_ITEM_DATA, + 'map_id', 'N', + '123') + with patch(PATCH_METHOD, new=fake_db) as req: + item = ExplicitRawMapModel.get(123) + actual = item.map_attr + for k, v in six.iteritems(map_native): + self.assertEqual(v, actual[k]) + +