DynamoDB in examples, Example 3: Toys store orders
Lets imagine that we working on online toys store. Current task is:
- track orders processing
- save user/product purchases for analysing in future
- save user purchases history
User order status may be equal to:
- in_progress
- completed
- canceled
We need next methods to modify order:
- create new order
- update order status
Methods to get orders:
- get user orders completed and in progress
- get product orders in_progress
- get product orders completed
- get product orders canceled
import datetime import random from uuid import uuid4 from ddb_table import DDBTable, DDBUUIDField, DDBIntField, DDBInt_IntField DDB_LOCAL_URL = 'http://localhost:8010' class DDBOrder(DDBTable): ORDER_IN_PROGRESS = 101 ORDER_COMPLETED = 201 ORDER_CANCELED = 301 ORDER_STATUS_CHOICES = [ ORDER_IN_PROGRESS, ORDER_COMPLETED, ORDER_CANCELED, ] TABLE_NAME = 'purchase' KEY_SCHEMA = [{ 'AttributeName': 'order_id', 'KeyType': 'HASH', }] PROVISIONED_THROUGHPUT = { 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1 } GLOBAL_SECONDARY_INDEXES = [{ 'IndexName': 'for_user_id', 'KeySchema': [{ 'AttributeName': 'user_id', 'KeyType': 'HASH' }, { 'AttributeName': 'order_status_created', 'KeyType': 'RANGE' } ], 'Projection': { 'ProjectionType': 'ALL', }, 'ProvisionedThroughput': { 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1, } }, { 'IndexName': 'for_product_id', 'KeySchema': [{ 'AttributeName': 'product_id', 'KeyType': 'HASH' }, { 'AttributeName': 'order_status_created', 'KeyType': 'RANGE' } ], 'Projection': { 'ProjectionType': 'ALL', }, 'ProvisionedThroughput': { 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1, } }] FIELDS = { 'order_id': DDBUUIDField, 'user_id': DDBUUIDField, 'product_id': DDBUUIDField, 'created': DDBIntField, 'order_status': DDBIntField, 'order_status_created': DDBInt_IntField, } def _get_endpoint_url(self): return DDB_LOCAL_URL def create_order(self, user_id, product_id): order_id = uuid4() created = datetime.datetime.utcnow().timestamp() order_status = self.ORDER_IN_PROGRESS order_status_created = '{order_status}_{created}'.format( order_status=order_status, created=created) data = { 'order_id': order_id, 'user_id': user_id, 'product_id': product_id, 'created': created, 'order_status': order_status, 'order_status_created': order_status_created, } response = self._dynamodb(operation='PutItem').call( TableName=self._get_table_name(), Item=self.encode_item(data=data)) return data def update_order_status(self, order_id, order_status): if order_status not in self.ORDER_STATUS_CHOICES: raise ValueError('Wrong order status.') response = self._dynamodb(operation='GetItem').call( TableName=self._get_table_name(), Key=self.encode_item(data={ 'order_id': order_id, })) item = self.decode_item(response['Item']) response = self._dynamodb(operation='UpdateItem').call( TableName=self._get_table_name(), Key=self.encode_item(data={ 'order_id': order_id, }), UpdateExpression='SET order_status = :order_status, order_status_created = :order_status_created', ExpressionAttributeValues={ ':order_status': { 'N': str(order_status), }, ':order_status_created': { 'S': '{order_status}_{created}'.format( order_status=order_status, created=item['created']), } }, ReturnValues='ALL_NEW') return self.decode_item(response['Attributes']) def get_user_orders(self, user_id, limit=10, last=None): """ Returns user orders with order_status in [self.ORDER_IN_PROGRESS, self.ORDER_COMPLETED] """ ddb_query = self._dynamodb(operation='Query') kwargs = { 'TableName': self._get_table_name(), 'IndexName': 'for_user_id', 'KeyConditions': { 'user_id': { 'AttributeValueList': [{ 'S': str(user_id), }], 'ComparisonOperator': 'EQ' }, 'order_status_created': { 'AttributeValueList': [{ 'S': '{order_status}_0'.format(order_status=self.ORDER_IN_PROGRESS - 1), }, { 'S': '{order_status}_0'.format(order_status=self.ORDER_COMPLETED + 1), }], 'ComparisonOperator': 'BETWEEN' }, }, 'Limit': limit } if last: kwargs['ExclusiveStartKey'] = last result = ddb_query.call(**kwargs) return ( [self.decode_item(item) for item in result.get('Items')], result.get('LastEvaluatedKey')) def get_product_orders(self, product_id, order_status, limit=10, last=None): ddb_query = self._dynamodb(operation='Query') kwargs = { 'TableName': self._get_table_name(), 'IndexName': 'for_product_id', 'KeyConditions': { 'product_id': { 'AttributeValueList': [{ 'S': str(product_id), }], 'ComparisonOperator': 'EQ' }, 'order_status_created': { 'AttributeValueList': [{ 'S': '{order_status}_0'.format(order_status=order_status - 1), }, { 'S': '{order_status}_0'.format(order_status=order_status + 1), }], 'ComparisonOperator': 'BETWEEN' }, }, 'Limit': limit } if last: kwargs['ExclusiveStartKey'] = last result = ddb_query.call(**kwargs) return ( [self.decode_item(item) for item in result.get('Items')], result.get('LastEvaluatedKey')) if __name__ == '__main__': ddb_order = DDBOrder() ddb_order.create_table() products = [] for i in range(10): products.append(str(uuid4())) users = [] for i in range(10): users.append(str(uuid4())) for i in range(20): result = ddb_order.create_order( user_id=random.choice(users), product_id=random.choice(products)) ddb_order.update_order_status( order_id=result['order_id'], order_status=random.choice(ddb_order.ORDER_STATUS_CHOICES)) user_orders = ddb_order.get_user_orders(user_id=users[0]) print(user_orders) orders_in_progress = ddb_order.get_product_orders( product_id=products[0], order_status=ddb_order.ORDER_IN_PROGRESS) print(orders_in_progress) # ([{'product_id': 'ad3ffb1e-f3eb-46bc-bebc-201034f757e6', 'user_id': 'e7aa25d8-2b1b-4fea-a735-4dbeeff06aaa', 'created': 1428791681, 'order_status_created': '201_1428791681', 'order_status': 201, 'order_id': 'f14e47c3-0232-430c-b79c-65b9ab000110'}], None) # ([{'product_id': '2d0126e0-92f7-437d-a938-41f95046d502', 'user_id': '917d107b-035e-4c02-a06b-343840fee92e', 'created': 1428791681, 'order_status_created': '101_1428791681', 'order_status': 101, 'order_id': '5ce48b08-2041-4d6b-82b4-7b1814d918ff'}, {'product_id': '2d0126e0-92f7-437d-a938-41f95046d502', 'user_id': '539f35ca-aed1-4353-80c2-0cd496bca092', 'created': 1428791681, 'order_status_created': '101_1428791681', 'order_status': 101, 'order_id': '3e5a8e0d-f76d-48ee-9c41-279f548f19cb'}], None)
Reserved words
Pay attention that I use order_status
instead of status
field name.
DynamoDB has many reserved words that can't be used inside UpdateExpression.
Licensed under CC BY-SA 3.0