Python Boto3和亚马逊DynamoDB编程教程
DynamoDB是AWS(亚马逊网络服务)提供的一种快速而灵活的NoSQL数据库服务。DynamoDB非常适合于移动应用、网络应用、物联网设备和游戏。Python对DynamoDB有很好的支持。在本教程中,我们将使用AWS PythonSDK(Boto3)对DynamoDB进行CRUD(创建、读取、更新、删除)操作。
前提条件
在学习本教程之前,你必须有DynamoDB的知识。
要开始学习本教程,你需要以下条件。
- DynamoDB本地:下载并配置DynamoDB。这个版本的DynamoDB仅用于开发目的。
- Python:下载并安装2.7或更高版本的Python。最新版本的Python可以在[官方网站]上下载。
- [集成开发环境]:使用你选择的IDE或代码编辑器。VS Code是一个不错的选择。
DynamoDB SDK介绍
AWS提供了一个与DynamoDB交互的SDK。SDK工具可用于不同的编程语言。
在本教程中,我们将学习如何使用AWS SDK for Python(Boto3)与DynamoDB交互。Boto3允许Python开发人员创建、配置和管理不同的AWS产品。
将AWS Python SDK (Boto3)与DynamoDB连接起来
在继续前进之前,请确保你满足先决条件。通过运行下面的命令安装最新版本的Boto3。这将安装Boto3 Python依赖项,这是我们的代码运行所需要的。
python -m pip install boto3
现在,我们将使用Python连接到DynamoDB的本地实例。我们将使用下面的代码来完成这个任务。注意endpoint_url 。
dynamodb = boto3.resource('dynamodb', endpoint_url="http://localhost:8000")
注意:为了使Python代码工作,我们必须使用下面的代码在我们的脚本中导入Boto3依赖。
import boto3
Boto3也可以用来连接AWS DynamoDB的在线实例(生产版本)。
使用Python SDK进行DynamoDB操作
在这个阶段,我们已经导入了Boto3库,并有一个本地版本的DynamoDB在运行。因此,我们可以编写Python脚本来对DynamoDB进行操作。第一步将是在我们的DynamoDB上创建一个表。在运行任何脚本之前,确保DynamoDB的本地实例正在你的电脑上运行。
创建表
我们将使用create_table 方法创建一个名为Devices 的表。该表的属性device_id 作为分区键,datacount 作为排序键。创建一个脚本并将其命名为create_table.py 。将下面的代码粘贴到脚本中。
import boto3 # import Boto3
def create_devices_table(dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Table defination
table = dynamodb.create_table(
TableName='Devices',
KeySchema=[
{
'AttributeName': 'device_id',
'KeyType': 'HASH' # Partition key
},
{
'AttributeName': 'datacount',
'KeyType': 'RANGE' # Sort key
}
],
AttributeDefinitions=[
{
'AttributeName': 'device_id',
# AttributeType defines the data type. 'S' is string type and 'N' is number type
'AttributeType': 'S'
},
{
'AttributeName': 'datacount',
'AttributeType': 'N'
},
],
ProvisionedThroughput={
# ReadCapacityUnits set to 10 strongly consistent reads per second
'ReadCapacityUnits': 10,
'WriteCapacityUnits': 10 # WriteCapacityUnits set to 10 writes per second
}
)
return table
if __name__ == '__main__':
device_table = create_devices_table()
# Print tablle status
print("Status:", device_table.table_status)
在上面的脚本中,首先要导入boto3的依赖关系。在每个连接到DynamoDB的脚本中都要导入这个依赖关系。我们也要连接到DynamoDB本地服务器。在脚本中,我们将定义表的结构。
只有分区键和排序键是必需的。请注意AttributeType 和ProvisionedThroughput 。AttributeType 定义了数据类型。ProvisionedThroughput 是一个应用程序在表上可以消耗的最大读写容量。
在AWS API文档中了解更多关于ProvisionedThroughput 。要运行该脚本,请输入以下命令。
python create_table.py
加载样本数据
让我们用一些数据来填充这个表。我们将通过使用函数put_item ,从一个JSON文件加载数据来实现这一目标。数据应该是JSON格式的,如下图所示。在JSONLint上验证数据是否为有效的JSON格式。将下面的数据保存在一个文件中,并将其命名为data.json 。
[
{
"device_id": "10001",
"datacount": 1,
"info": {
"info_timestamp": "1612519200",
"temperature1": 37.2,
"temperature2": 21.31,
"temperature3": 25.6,
"temperature4": 22.96,
"temperature5": 24.69
}
},
{
"device_id": "10001",
"datacount": 2,
"info": {
"info_timestamp": "1612521000",
"temperature1": 24.34,
"temperature2": 24.59,
"temperature3": 19.2,
"temperature4": 29.11,
"temperature5": 23.18
}
},
{
"device_id": "10002",
"datacount": 1,
"info": {
"info_timestamp": "1612519200",
"temperature1": 14.34,
"temperature2": 17.59,
"temperature3": 11.2,
"temperature4": 15.95,
"temperature5": 16.17
}
},
{
"device_id": "10002",
"datacount": 2,
"info": {
"info_timestamp": "1612521000",
"temperature1": 13.04,
"temperature2": 15.01,
"temperature3": 18.91,
"temperature4": 16.45,
"temperature5": 16.21
}
},
{
"device_id": "10003",
"datacount": 1,
"info": {
"info_timestamp": "1612519200",
"temperature1": 34.23,
"temperature2": 36.21,
"temperature3": 31.24,
"temperature4": 32.02,
"temperature5": 29.54
}
},
{
"device_id": "10003",
"datacount": 2,
"info": {
"info_timestamp": "1612521000",
"temperature1": 34.55,
"temperature2": 33.13,
"temperature3": 32.62,
"temperature4": 39.32,
"temperature5": 38.87
}
}
]
创建一个名为load_data.py 的脚本并添加以下代码。该代码从JSON文件data.json 中加载数据,并将其插入到Devices 表中。
import json # module for converting Python objects to JSON
# decimal module support correctly-rounded decimal floating point arithmetic.
from decimal import Decimal
import boto3 # import Boto3
def load_data(devices, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
devices_table = dynamodb.Table('Devices')
# Loop through all the items and load each
for device in devices:
device_id = (device['device_id'])
datacount = device['datacount']
# Print device info
print("Loading Devices Data:", device_id, datacount)
devices_table.put_item(Item=device)
if __name__ == '__main__':
# open file and read all the data in it
with open("data.json") as json_file:
device_list = json.load(json_file, parse_float=Decimal)
load_data(device_list)
要执行该脚本,运行下面的命令。
python load_data.py
下面是数据加载过程成功后的预期响应。
Adding Device Data: 10001 1
Adding Device Data: 10001 2
Adding Device Data: 10002 1
Adding Device Data: 10002 2
Adding Device Data: 10003 1
Adding Device Data: 10003 2
创建项目
我们使用put_item 方法在我们的表中插入项目。我们将创建一个脚本,在表Devices 中插入/创建一个新项目。创建一个名为create_item.py 的脚本并粘贴下面的代码。
from pprint import pprint # import pprint, a module that enable to “pretty-print”
import boto3 # import Boto3
def put_device(device_id, datacount, timestamp, temperature1, temperature2, temperature3, temperature4, temperature5, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table
devices_table = dynamodb.Table('Devices')
response = devices_table.put_item(
# Data to be inserted
Item={
'device_id': device_id,
'datacount': datacount,
'info': {
'info_timestamp': timestamp,
'temperature1': temperature1,
'temperature2': temperature2,
'temperature3': temperature3,
'temperature4': temperature4,
'temperature5': temperature5
}
}
)
return response
if __name__ == '__main__':
device_resp = put_device("10001", 3, "1612522800",
"23.74", "32.56", "12.43", "44.74", "12.74")
print("Create item successful.")
# Print response
pprint(device_resp)
运行下面的命令来执行脚本create_item.py 。
python create_item.py
我们刚刚添加了下面这个项目。
{
"device_id": "10001",
"datacount": 3,
"info": {
"info_timestamp": "1612522800",
"temperature1": 23.74,
"temperature2": 23.74,
"temperature3": 12.43,
"temperature4": 44.74,
"temperature5": 12.74
}
}
读取项目
我们将使用get_item 方法读取我们刚刚创建的项目。我们需要指定我们要读取的项目的主键。在这种情况下,Devices 表的主键是一个分区键和一个排序键的组合。主键是device_id ,而排序键是datacount 。
# import Boto3 exceptions and error handling module
from botocore.exceptions import ClientError
import boto3 # import Boto3
def get_device(device_id, datacount, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table to read from
devices_table = dynamodb.Table('Devices')
try:
response = devices_table.get_item(
Key={'device_id': device_id, 'datacount': datacount})
except ClientError as e:
print(e.response['Error']['Message'])
else:
return response['Item']
if __name__ == '__main__':
device = get_device("10001", 3,)
if device:
print("Get Device Data Done:")
# Print the data read
print(device)
运行下面的命令来执行脚本read_item.py 。
python read_item.py
下面是预期的输出。你可以确认响应的项目是我们之前创建的项目。使用特定的主键,我们可以检索到一个特定的项目。
Get Device Data Done:
{'datacount': Decimal('3'),
'device_id': '10001',
'info': {'info_timestamp': '1612522800',
'temperature1': '23.74',
'temperature2': '32.56',
'temperature3': '12.43',
'temperature4': '44.74',
'temperature5': '12.74'}}
条件
DynamoDB有一个使用条件的规定。在更新或删除项目时,可以应用条件。我们可以提供一个ConditionExpression 。
如果ConditionExpression ,评估为真,那么就会执行该动作。熟悉一下不同的DynamoDB条件。
更新
更新是指通过更新现有属性值、删除属性或添加新属性来修改先前创建的项目。在本教程中,我们将更新现有属性的值。下面是原始项目和更新后的项目。
原始项目
{
"device_id": "10001",
"datacount": 3,
"info": {
"info_timestamp": "1612522800",
"temperature1": 23.74,
"temperature2": 23.74,
"temperature3": 12.43,
"temperature4": 44.74,
"temperature5": 12.74
}
}
更新的项目
{
"device_id": "10001",
"datacount": 3,
"info": {
"info_timestamp": "1612522800",
"temperature1": 33.74,
"temperature2": 23.74,
"temperature3": 25.2,
"temperature4": 22.0,
"temperature5": 25.0
}
}
我们将使用update_item 方法,如下面的代码所示。创建一个名为update_item.py 的脚本,添加下面的代码。
from pprint import pprint # import pprint, a module that enable to “pretty-print”
import boto3 # import Boto3
def update_device(device_id, datacount, info_timestamp, temperature1, temperature2, temperature3, temperature4, temperature5, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table
devices_table = dynamodb.Table('Devices')
response = devices_table.update_item(
Key={
'device_id': device_id,
'datacount': datacount
},
UpdateExpression="set info.info_timestamp=:time, info.temperature1=:t1, info.temperature2=:t2, info.temperature3=:t3, info.temperature4=:t4, info.temperature5=:t5",
ExpressionAttributeValues={
':time': info_timestamp,
':t1': temperature1,
':t2': temperature2,
':t3': temperature3,
':t4': temperature4,
':t5': temperature5
},
ReturnValues="UPDATED_NEW"
)
return response
if __name__ == '__main__':
update_response = update_device(
"10001", 3, "1612522800", "33.74", "23.74", "25.20", "22.00", "25.00")
print("Device Updated")
# Print response
pprint(update_response)
运行下面的命令来执行脚本update_item.py 。
python update_item.py
下面是预期的输出。
{'Attributes': {'info': {'info_timestamp': '1612522800',
'temperature1': '33.74',
'temperature2': '23.74',
'temperature3': '25.20',
'temperature4': '22.00',
'temperature5': '25.00'}},
'ResponseMetadata': {'HTTPHeaders': {'content-length': '212',
'content-type': 'application/x-amz-json-1.0',
'date': 'Fri, 05 Feb 2021 11:27:43 GMT',
'server': 'Jetty(9.4.18.v20190429)',
'x-amz-crc32': '1118861638',
'x-amzn-requestid': 'a6a8201d-dc10-4837-be6a-7de03ee9b24f'},
'HTTPStatusCode': 200,
'RequestId': 'a6a8201d-dc10-4837-be6a-7de03ee9b24f',
'RetryAttempts': 0}}
删除项目
要删除一个项目,我们使用delete_item 方法。我们可以指定要删除的项目的主键或者提供一个ConditionExpression 。如果我们使用一个ConditionExpression ,除非条件被评估为True,否则该项目将不会被删除。
在这个例子中,我们将为要删除的项目提供一个主键,并提供一个ConditionExpression 。如果满足ConditionExpression ,该项目将被删除。
在这个例子中,条件是。
ConditionExpression="info.info_timestamp >= :val"
我们将删除下面这个项目。
{
"device_id": "10001",
"datacount": 3,
"info": {
"info_timestamp": "1612522800",
"temperature1": 33.74,
"temperature2": 23.74,
"temperature3": 25.2,
"temperature4": 22.0,
"temperature5": 25.0
}
}
如果info_timestamp 的值大于或等于所提供的值,该项目将被删除。创建一个名为delete_item.py 的脚本,并粘贴下面的代码。
# import Boto3 exceptions and error handling module
from botocore.exceptions import ClientError
from pprint import pprint # import pprint, a module that enable to “pretty-print”
import boto3 # import Boto3
def delete_device(device_id, datacount, info_timestamp, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table to delete from
devices_table = dynamodb.Table('Devices')
try:
response = devices_table.delete_item(
Key={
'device_id': device_id,
'datacount': datacount
},
# Conditional request
ConditionExpression="info.info_timestamp <= :value",
ExpressionAttributeValues={
":value": info_timestamp
}
)
except ClientError as er:
if er.response['Error']['Code'] == "ConditionalCheckFailedException":
print(er.response['Error']['Message'])
else:
raise
else:
return response
if __name__ == '__main__':
print("DynamoBD Conditional delete")
# Provide device_id, datacount, info_timestamp
delete_response = delete_device("10001", 3, "1712519200")
if delete_response:
print("Item Deleted:")
# Print response
pprint(delete_response)
运行下面的命令来执行脚本delete_item.py 。
python delete_item.py
如果不满足ConditionExpression ,预期的响应将如下所示。
Conditional delete
The conditional request failed
如果条件被删除或满足,那么该项目将被成功删除。
查询
查询返回所有符合分区键值的项目。在这个例子中,我们将查询一个特定分区键的所有数据。我们需要指定分区键值。
在这个例子中,分区键是device_id 。我们将查询所有device_id 等于10001的项目。
import boto3 # import Boto3
from boto3.dynamodb.conditions import Key # import Boto3 conditions
def query_devices(device_id, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table to query
devices_table = dynamodb.Table('Devices')
response = devices_table.query(
KeyConditionExpression=Key('device_id').eq(device_id)
)
return response['Items']
if __name__ == '__main__':
query_id = "10001"
print(f"Device Data from Device ID: {query_id}")
devices_data = query_devices(query_id)
# Print the items returned
for device_data in devices_data:
print(device_data['device_id'], ":", device_data['datacount'])
运行下面的命令来执行脚本query.py 。
python query.py
扫描
扫描操作读取并返回表中的所有项目。方法DynamoDB.Table.scan() 是用来扫描表的。使用filter_expression ,我们可以过滤要返回的项目。
然而,整个表将被扫描,不符合filter_expression 的项目将被丢弃。创建一个名为scan.py 的脚本,并粘贴下面的代码。
import boto3 # import Boto3
def scan_devices(display_devices_data, dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# Specify the table to scan
devices_table = dynamodb.Table('Devices')
done = False
start_key = None
while not done:
if start_key:
scan_kwargs['ExclusiveStartKey'] = start_key
response = devices_table.scan()
display_devices_data(response.get('Items', []))
start_key = response.get('LastEvaluatedKey', None)
done = start_key is None
if __name__ == '__main__':
# A method for printing the items
def print_devices(devices):
for device in devices:
print(f"\n{device['device_id']} : {device['datacount']}")
print(device['info'])
print(
f"Scanning all devices data")
# Print the items returned
scan_devices(print_devices)
上面的脚本扫描了Devices 表,没有filter_expression 。运行下面的命令来执行脚本scan.py。输出将是Devices 表中的所有项目。
python scan.py
删除表
要删除一个表,我们使用方法DynamoDB.Table.delete() 。我们所需要的是指定表名。这个动作很少被执行。创建一个名为delete_table.py 的脚本,并添加下面的代码。
import boto3 # import Boto3
def delete_devices_table(dynamodb=None):
dynamodb = boto3.resource(
'dynamodb', endpoint_url="http://localhost:8000")
# specify the table to be deleted
devices_table = dynamodb.Table('Devices')
devices_table.delete()
if __name__ == '__main__':
delete_devices_table()
print("Table deleted.")
运行下面的命令来执行该脚本delete_table.py 。
python delete_table.py
总结
我们已经学会了如何使用AWS SDK for Python、Boto3来编写与AWS DynamoDB交互的python脚本。