Skip to content

Commit

Permalink
Make model serialize/deserialize methods public.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpinner-lyft committed Oct 7, 2020
1 parent f1e5ab3 commit fc91061
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 25 deletions.
3 changes: 2 additions & 1 deletion docs/release_notes.rst
Expand Up @@ -21,7 +21,8 @@ Items written using other formats must be rewritten before upgrading.

** Model Serialization **

The ``Model._serialize`` method has changed and now only returns a dictionary of the DynamoDB attribute values.
THe ``Model`` class now includes public methods for serializing and deserializing its attributes.
``Model.serialize`` and ``Model.deserialize`` convert the model to/from a dictionary of DynamoDB attribute values.

Other changes in this release:

Expand Down
8 changes: 4 additions & 4 deletions pynamodb/attributes.py
Expand Up @@ -341,7 +341,7 @@ def _set_attributes(self, **attributes: Attribute) -> None:
raise ValueError("Attribute {} specified does not exist".format(attr_name))
setattr(self, attr_name, attr_value)

def _serialize(self, null_check=True) -> Dict[str, Dict[str, Any]]:
def serialize(self, null_check=True) -> Dict[str, Dict[str, Any]]:
"""
Serialize attribute values for DynamoDB
"""
Expand All @@ -359,7 +359,7 @@ def _serialize(self, null_check=True) -> Dict[str, Dict[str, Any]]:
attribute_values[attr.attr_name] = {attr.attr_type: attr_value}
return attribute_values

def _deserialize(self, attribute_values: Dict[str, Dict[str, Any]]) -> None:
def deserialize(self, attribute_values: Dict[str, Dict[str, Any]]) -> None:
"""
Sets attributes sent back from DynamoDB on this object
"""
Expand Down Expand Up @@ -917,7 +917,7 @@ def serialize(self, values):
setattr(instance, name, values[name])
values = instance

return values._serialize()
return AttributeContainer.serialize(values)

# Continue to serialize NULL values in "raw" map attributes for backwards compatibility.
# This special case behavior for "raw" attributes should be removed in the future.
Expand Down Expand Up @@ -948,7 +948,7 @@ def deserialize(self, values):
discriminator_value = discriminator_attr.get_value(discriminator_attribute_value)
cls = discriminator_attr.deserialize(discriminator_value)
instance = cls()
instance._deserialize(values)
AttributeContainer.deserialize(instance, values)
return instance

return {
Expand Down
10 changes: 5 additions & 5 deletions pynamodb/models.py
Expand Up @@ -120,7 +120,7 @@ def commit(self) -> None:
delete_items = []
for item in self.pending_operations:
if item['action'] == PUT:
put_items.append(item['item']._serialize())
put_items.append(item['item'].serialize())
elif item['action'] == DELETE:
delete_items.append(item['item']._get_keys())
self.pending_operations = []
Expand Down Expand Up @@ -414,7 +414,7 @@ def update(self, actions: Sequence[Action], condition: Optional[Condition] = Non
kwargs.update(actions=actions)

data = self._get_connection().update_item(*args, **kwargs)
self._deserialize(data[ATTRIBUTES])
self.deserialize(data[ATTRIBUTES])
return data

def save(self, condition: Optional[Condition] = None) -> Dict[str, Any]:
Expand Down Expand Up @@ -443,7 +443,7 @@ def refresh(self, consistent_read: bool = False) -> None:
item_data = attrs.get(ITEM, None)
if item_data is None:
raise self.DoesNotExist("This item does not exist in the table.")
self._deserialize(item_data)
self.deserialize(item_data)

def get_operation_kwargs_from_instance(
self,
Expand Down Expand Up @@ -532,7 +532,7 @@ def from_raw_data(cls: Type[_T], data: Dict[str, Any]) -> _T:
raise ValueError("Received no data to construct object")

model = cls(_user_instantiated=False)
model._deserialize(data)
model.deserialize(data)
return model

@classmethod
Expand Down Expand Up @@ -886,7 +886,7 @@ def _get_save_args(self, attributes=True, null_check=True):
:param null_check: If True, then attributes are checked for null.
"""
kwargs = {}
attribute_values = self._serialize(null_check)
attribute_values = self.serialize(null_check)
hash_key_attribute = self._hash_key_attribute()
hash_key = attribute_values.pop(hash_key_attribute.attr_name, {}).get(hash_key_attribute.attr_type)
range_key = None
Expand Down
2 changes: 1 addition & 1 deletion tests/test_discriminator.py
Expand Up @@ -44,7 +44,7 @@ def test_serialize(self):
dtm.hash_key = 'foo'
dtm.value = StringValue(name='foo', value='Hello')
dtm.values = [NumberValue(name='bar', value=5), RenamedValue(name='baz', value='World')]
assert dtm._serialize() == {
assert dtm.serialize() == {
'hash_key': {'S': 'foo'},
'value': {'M': {'cls': {'S': 'StringValue'}, 'name': {'S': 'foo'}, 'value': {'S': 'Hello'}}},
'values': {'L': [
Expand Down
28 changes: 14 additions & 14 deletions tests/test_model.py
Expand Up @@ -1406,7 +1406,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo', UserModel.user_id < 'id-1'):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

with patch(PATCH_METHOD) as req:
Expand All @@ -1418,7 +1418,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo', UserModel.user_id >= 'id-1'):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

with patch(PATCH_METHOD) as req:
Expand All @@ -1430,7 +1430,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo', UserModel.user_id <= 'id-1'):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

with patch(PATCH_METHOD) as req:
Expand All @@ -1442,7 +1442,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo', UserModel.user_id == 'id-1'):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

with patch(PATCH_METHOD) as req:
Expand All @@ -1454,7 +1454,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo', UserModel.user_id.startswith('id')):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

with patch(PATCH_METHOD) as req:
Expand All @@ -1466,7 +1466,7 @@ def test_query(self):
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
queried = []
for item in UserModel.query('foo'):
queried.append(item._serialize())
queried.append(item.serialize())
self.assertTrue(len(queried) == len(items))

def fake_query(*args):
Expand Down Expand Up @@ -1512,7 +1512,7 @@ def fake_query(*args):
'foo',
UserModel.user_id.startswith('id'),
UserModel.email.contains('@') & UserModel.picture.exists() & UserModel.zip_code.between(2, 3)):
queried.append(item._serialize())
queried.append(item.serialize())
params = {
'KeyConditionExpression': '(#0 = :0 AND begins_with (#1, :1))',
'FilterExpression': '((contains (#2, :2) AND attribute_exists (#3)) AND #4 BETWEEN :3 AND :4)',
Expand Down Expand Up @@ -2066,7 +2066,7 @@ def test_index_queries(self):
queried = []

for item in IndexedModel.email_index.query('foo', filter_condition=IndexedModel.user_name.startswith('bar'), limit=2):
queried.append(item._serialize())
queried.append(item.serialize())

params = {
'KeyConditionExpression': '#0 = :0',
Expand Down Expand Up @@ -2104,7 +2104,7 @@ def test_index_queries(self):
'foo',
filter_condition=LocalIndexedModel.user_name.startswith('bar') & LocalIndexedModel.aliases.contains('baz'),
limit=1):
queried.append(item._serialize())
queried.append(item.serialize())

params = {
'KeyConditionExpression': '#0 = :0',
Expand Down Expand Up @@ -2145,7 +2145,7 @@ def test_index_queries(self):
'foo',
filter_condition=CustomAttrNameModel.overidden_user_name.startswith('bar'),
limit=2):
queried.append(item._serialize())
queried.append(item.serialize())

params = {
'KeyConditionExpression': '#0 = :0',
Expand Down Expand Up @@ -2789,7 +2789,7 @@ def test_explicit_raw_map_serialize_pass(self):
map_native = {'foo': 'bar'}
map_serialized = {'M': {'foo': {'S': 'bar'}}}
instance = ExplicitRawMapModel(map_attr=map_native)
serialized = instance._serialize()
serialized = instance.serialize()
self.assertEqual(serialized['map_attr'], map_serialized)

def test_raw_map_serialize_fun_one(self):
Expand All @@ -2805,7 +2805,7 @@ def test_raw_map_serialize_fun_one(self):
'bool_type': {'BOOL': True}}}

instance = ExplicitRawMapModel(map_attr=map_native)
serialized = instance._serialize()
serialized = instance.serialize()
actual = serialized['map_attr']
self.assertEqual(expected, actual)

Expand All @@ -2827,7 +2827,7 @@ def test_raw_map_deserializes(self):
}
}
instance = ExplicitRawMapModel()
instance._deserialize({'map_attr': map_serialized})
instance.deserialize({'map_attr': map_serialized})
actual = instance.map_attr
for k, v in map_native.items():
self.assertEqual(v, actual[k])
Expand Down Expand Up @@ -2866,7 +2866,7 @@ def test_raw_map_as_sub_map_serialize_pass(self):
map_field=map_native
)
)
serialized = instance._serialize()
serialized = instance.serialize()
self.assertEqual(serialized['sub_attr']['M']['map_field'], map_serialized)

def _get_raw_map_as_sub_map_test_data(self):
Expand Down

0 comments on commit fc91061

Please sign in to comment.