Skip to content

Commit e246ae6

Browse files
authored
Make model serialize/deserialize methods public. (#860)
1 parent f1e5ab3 commit e246ae6

File tree

5 files changed

+26
-25
lines changed

5 files changed

+26
-25
lines changed

docs/release_notes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Items written using other formats must be rewritten before upgrading.
2121

2222
** Model Serialization **
2323

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

2627
Other changes in this release:
2728

pynamodb/attributes.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def _set_attributes(self, **attributes: Attribute) -> None:
341341
raise ValueError("Attribute {} specified does not exist".format(attr_name))
342342
setattr(self, attr_name, attr_value)
343343

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

362-
def _deserialize(self, attribute_values: Dict[str, Dict[str, Any]]) -> None:
362+
def deserialize(self, attribute_values: Dict[str, Dict[str, Any]]) -> None:
363363
"""
364364
Sets attributes sent back from DynamoDB on this object
365365
"""
@@ -917,7 +917,7 @@ def serialize(self, values):
917917
setattr(instance, name, values[name])
918918
values = instance
919919

920-
return values._serialize()
920+
return AttributeContainer.serialize(values)
921921

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

954954
return {

pynamodb/models.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def commit(self) -> None:
120120
delete_items = []
121121
for item in self.pending_operations:
122122
if item['action'] == PUT:
123-
put_items.append(item['item']._serialize())
123+
put_items.append(item['item'].serialize())
124124
elif item['action'] == DELETE:
125125
delete_items.append(item['item']._get_keys())
126126
self.pending_operations = []
@@ -414,7 +414,7 @@ def update(self, actions: Sequence[Action], condition: Optional[Condition] = Non
414414
kwargs.update(actions=actions)
415415

416416
data = self._get_connection().update_item(*args, **kwargs)
417-
self._deserialize(data[ATTRIBUTES])
417+
self.deserialize(data[ATTRIBUTES])
418418
return data
419419

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

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

534534
model = cls(_user_instantiated=False)
535-
model._deserialize(data)
535+
model.deserialize(data)
536536
return model
537537

538538
@classmethod
@@ -886,7 +886,7 @@ def _get_save_args(self, attributes=True, null_check=True):
886886
:param null_check: If True, then attributes are checked for null.
887887
"""
888888
kwargs = {}
889-
attribute_values = self._serialize(null_check)
889+
attribute_values = self.serialize(null_check)
890890
hash_key_attribute = self._hash_key_attribute()
891891
hash_key = attribute_values.pop(hash_key_attribute.attr_name, {}).get(hash_key_attribute.attr_type)
892892
range_key = None

tests/test_discriminator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_serialize(self):
4444
dtm.hash_key = 'foo'
4545
dtm.value = StringValue(name='foo', value='Hello')
4646
dtm.values = [NumberValue(name='bar', value=5), RenamedValue(name='baz', value='World')]
47-
assert dtm._serialize() == {
47+
assert dtm.serialize() == {
4848
'hash_key': {'S': 'foo'},
4949
'value': {'M': {'cls': {'S': 'StringValue'}, 'name': {'S': 'foo'}, 'value': {'S': 'Hello'}}},
5050
'values': {'L': [

tests/test_model.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ def test_query(self):
14061406
req.return_value = {'Count': len(items), 'ScannedCount': len(items), 'Items': items}
14071407
queried = []
14081408
for item in UserModel.query('foo', UserModel.user_id < 'id-1'):
1409-
queried.append(item._serialize())
1409+
queried.append(item.serialize())
14101410
self.assertTrue(len(queried) == len(items))
14111411

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

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

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

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

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

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

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

20712071
params = {
20722072
'KeyConditionExpression': '#0 = :0',
@@ -2104,7 +2104,7 @@ def test_index_queries(self):
21042104
'foo',
21052105
filter_condition=LocalIndexedModel.user_name.startswith('bar') & LocalIndexedModel.aliases.contains('baz'),
21062106
limit=1):
2107-
queried.append(item._serialize())
2107+
queried.append(item.serialize())
21082108

21092109
params = {
21102110
'KeyConditionExpression': '#0 = :0',
@@ -2145,7 +2145,7 @@ def test_index_queries(self):
21452145
'foo',
21462146
filter_condition=CustomAttrNameModel.overidden_user_name.startswith('bar'),
21472147
limit=2):
2148-
queried.append(item._serialize())
2148+
queried.append(item.serialize())
21492149

21502150
params = {
21512151
'KeyConditionExpression': '#0 = :0',
@@ -2789,7 +2789,7 @@ def test_explicit_raw_map_serialize_pass(self):
27892789
map_native = {'foo': 'bar'}
27902790
map_serialized = {'M': {'foo': {'S': 'bar'}}}
27912791
instance = ExplicitRawMapModel(map_attr=map_native)
2792-
serialized = instance._serialize()
2792+
serialized = instance.serialize()
27932793
self.assertEqual(serialized['map_attr'], map_serialized)
27942794

27952795
def test_raw_map_serialize_fun_one(self):
@@ -2805,7 +2805,7 @@ def test_raw_map_serialize_fun_one(self):
28052805
'bool_type': {'BOOL': True}}}
28062806

28072807
instance = ExplicitRawMapModel(map_attr=map_native)
2808-
serialized = instance._serialize()
2808+
serialized = instance.serialize()
28092809
actual = serialized['map_attr']
28102810
self.assertEqual(expected, actual)
28112811

@@ -2827,7 +2827,7 @@ def test_raw_map_deserializes(self):
28272827
}
28282828
}
28292829
instance = ExplicitRawMapModel()
2830-
instance._deserialize({'map_attr': map_serialized})
2830+
instance.deserialize({'map_attr': map_serialized})
28312831
actual = instance.map_attr
28322832
for k, v in map_native.items():
28332833
self.assertEqual(v, actual[k])
@@ -2866,7 +2866,7 @@ def test_raw_map_as_sub_map_serialize_pass(self):
28662866
map_field=map_native
28672867
)
28682868
)
2869-
serialized = instance._serialize()
2869+
serialized = instance.serialize()
28702870
self.assertEqual(serialized['sub_attr']['M']['map_field'], map_serialized)
28712871

28722872
def _get_raw_map_as_sub_map_test_data(self):

0 commit comments

Comments
 (0)