You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地DynamoDB插入遇ValidationException:TransactWriteItems缺必填键

问题排查与解决方案

核心错误原因

  • 缺失表主键值:你的Profile表主键是email(HASH键),但在transact_write_items的第二个Put操作中,Item只包含pk,未提供必填的email键——DynamoDB要求每个Item必须包含主键属性,这是触发ValidationException的直接原因。
  • 引用不存在的字段:测试数据仅包含email,但代码中尝试访问profile['username'],会引发KeyError(当前被主键错误掩盖)。
  • ConditionExpression逻辑错误:表主键是email,但你用了attribute_not_exists(pk)作为条件,pk并非表的主键或预定义属性,逻辑与表结构不匹配。
  • Mock与本地实例冲突:使用@mock_dynamodb装饰器后,应在测试的setUp中创建模拟表,而非直接连接本地DynamoDB实例,二者不能混用。

修复步骤

1. 修正TransactItems的Put操作,确保每个Item包含主键

基于你的表结构,每个写入的Item必须包含email。如果要实现username的唯一约束,需调整逻辑(参考官方博客的设计可通过全局二级索引实现),先修复主键问题:

def insertProfile(self, profile):
    message = f"Profile was not created"
    # 使用moto mock时无需指定endpoint_url,moto会自动模拟客户端
    dynamodb = boto3.client('dynamodb')
    response = dynamodb.transact_write_items(
            TransactItems=[
                {
                    'Put': {
                        'TableName': 'Profile',
                        'Item':{
                            'email' : {
                                'S': profile['email'],   
                            }
                        },
                        # 改为检查主键email是否已存在
                        'ConditionExpression':'attribute_not_exists(email)'
                    },
                    'Put': {
                        'TableName': 'Profile',
                        'Item':{
                            'email' : {
                                'S': profile['email'],   
                            },
                            # 处理username缺失的情况,用邮箱前缀作为默认值
                            'pk': {
                                'S': f"username#{profile.get('username', profile['email'].split('@')[0])}"
                            }
                        },
                        'ConditionExpression':'attribute_not_exists(pk)'
                    }
                }
            ]
        )
        
    return {
        "status_code": response['ResponseMetadata']['HTTPStatusCode'],
        "message": message
    }

2. 补充测试数据或处理字段缺失

修改测试数据event.json,添加username字段:

{ 
    "email": "email@b.com",
    "username": "testuser"
}

或在代码中通过get方法设置默认值(如上面代码所示),避免KeyError。

3. 正确使用moto Mock功能

在setUp中创建模拟表,不再依赖本地DynamoDB实例:

def setUp(self) -> None:
    self.logging = logging.getLogger(__name__)
    self.re_dict = {
        "email": "(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)",
        # 若需验证username,添加对应正则
        "username": "^[a-zA-Z0-9_]{3,20}$"
    }
    # 创建模拟DynamoDB表,若要实现username唯一,添加全局二级索引
    dynamodb = boto3.client('dynamodb')
    dynamodb.create_table(
        TableName='Profile',
        AttributeDefinitions=[
            {'AttributeName': 'email', 'AttributeType': 'S'},
            {'AttributeName': 'pk', 'AttributeType': 'S'}
        ],
        KeySchema=[
            {'AttributeName': 'email', 'KeyType': 'HASH'}
        ],
        # 添加唯一GSI实现username的唯一性约束
        GlobalSecondaryIndexes=[
            {
                'IndexName': 'pk-index',
                'KeySchema': [{'AttributeName': 'pk', 'KeyType': 'HASH'}],
                'Projection': {'ProjectionType': 'ALL'},
                'ProvisionedThroughput': {'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1},
                'IndexStatus': 'ACTIVE'
            }
        ],
        ProvisionedThroughput={'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1}
    )

def testHandler(self) -> None:
    response = {
        "status_code": HTTPStatus.BAD_REQUEST,
        "message": "Creation was not successful."
    }
    # 直接构造测试event,无需读取文件,更便于测试
    event = { 
        "email": "email@b.com",
        "username": "testuser"
    }
    self.logging.info(event)

    is_valid, profileData = self.validateProfile(event)
    self.assertEqual(True, is_valid) 
    response = self.insertProfile(profileData)
    # 注意response是字典,需取status_code字段断言
    self.assertEqual(HTTPStatus.OK, response['status_code']) 

4. 调整唯一约束逻辑

若要实现username的唯一性,需依赖全局二级索引的唯一性配置,此时ConditionExpression应检查GSI的键是否存在,而非非主键属性pk。

总结

当前最直接的触发错误是第二个Put操作的Item缺失表主键email,导致DynamoDB验证失败。同时需调整Mock使用方式,避免与本地实例冲突,并处理字段缺失问题。

内容的提问来源于stack exchange,提问作者Hardrock302

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:31:09