diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index e7277ee6b..4832d7944 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -121,6 +121,14 @@ class Item(object): # TODO deal with other types self.attrs[key] = DynamoType({"S": value}) + def update_with_attribute_updates(self, attribute_updates): + for attribute_name, update_action in attribute_updates.items(): + action = update_action['Action'] + new_value = update_action['Value'].values()[0] + if action == 'PUT': + # TODO deal with other types + self.attrs[attribute_name] = DynamoType({"S": new_value}) + class Table(object): @@ -411,12 +419,19 @@ class DynamoDBBackend(BaseBackend): return table.scan(scan_filters) - def update_item(self, table_name, key, update_expression): + def update_item(self, table_name, key, update_expression, attribute_updates): table = self.get_table(table_name) + if table.hash_key_attr in key: + # Sometimes the key is wrapped in a dict with the key name + key = key[table.hash_key_attr] + hash_value = DynamoType(key) item = table.get_item(hash_value) - item.update(update_expression) + if update_expression: + item.update(update_expression) + else: + item.update_with_attribute_updates(attribute_updates) return item def delete_item(self, table_name, keys): diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index 57d06bbf3..2be0dda8f 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -373,8 +373,9 @@ class DynamoHandler(BaseResponse): def update_item(self): name = self.body['TableName'] key = self.body['Key'] - update_expression = self.body['UpdateExpression'] - item = dynamodb_backend2.update_item(name, key, update_expression) + update_expression = self.body.get('UpdateExpression') + attribute_updates = self.body.get('AttributeUpdates') + item = dynamodb_backend2.update_item(name, key, update_expression, attribute_updates) item_dict = item.to_json() item_dict['ConsumedCapacityUnits'] = 0.5 diff --git a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py index 808805b8d..6baeb8a12 100644 --- a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py +++ b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py @@ -122,6 +122,33 @@ def test_item_add_and_describe_and_update(): }) +@requires_boto_gte("2.9") +@mock_dynamodb2 +def test_item_partial_save(): + table = create_table() + + data = { + 'forum_name': 'LOLCat Forum', + 'Body': 'http://url_to_lolcat.gif', + 'SentBy': 'User A', + } + + table.put_item(data=data) + returned_item = table.get_item(forum_name="LOLCat Forum") + + returned_item['SentBy'] = 'User B' + returned_item.partial_save() + + returned_item = table.get_item( + forum_name='LOLCat Forum' + ) + dict(returned_item).should.equal({ + 'forum_name': 'LOLCat Forum', + 'Body': 'http://url_to_lolcat.gif', + 'SentBy': 'User B', + }) + + @requires_boto_gte("2.9") @mock_dynamodb2 def test_item_put_without_table():