【问题标题】:How do I conditionally insert an item into a dynamodb table using boto3如何使用 boto3 有条件地将项目插入 dynamodb 表
【发布时间】:2018-11-15 20:59:27
【问题描述】:

如果我有一个带有 userId 哈希键和 productId 范围键的表,我如何使用 boto3 的 dynamodb 绑定将项目放入该表中?

对 put_item 的正常调用如下所示

table.put_item(Item={'userId': 1, 'productId': 2})

我的 ConditionExpression 调用如下所示:

table.put_item(
    Item={'userId': 1, 'productId': 2}, 
    ConditionExpression='userId <> :uid AND productId <> :pid', 
    ExpressionAttributeValues={':uid': 1, ':pid': 3}
)

但这每次都会引发 ConditionalCheckFailedException。是否存在具有相同 productId 的项目。

【问题讨论】:

    标签: python amazon-dynamodb boto3


    【解决方案1】:

    不幸的是,这方面的文档不是很清楚。我需要完成类似的事情,这对我有用,使用 boto3:

    try:
        table.put_item(
            Item={
                'foo':1,
                'bar':2,
            },
            ConditionExpression='attribute_not_exists(foo) AND attribute_not_exists(bar)'
        )
    except botocore.exceptions.ClientError as e:
        # Ignore the ConditionalCheckFailedException, bubble up
        # other exceptions.
        if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
            raise
    

    与另一个答案类似,关键在于 attribute_not_exists 函数,但我最初不清楚如何让它工作。经过一些实验,我能够按照上述方式进行操作。

    【讨论】:

    • 如果您添加此导入from boto3.dynamodb.conditions import Attr,那么ConditionExpression 可以是ConditionExpression=Attr("foo").ne(1) &amp; Attr("bar").ne(2)ConditionExpression = Attr("foo").not_exists() &amp; Attr("bar").not_exists() 之一,我发现not_exists() 的名称令人困惑。它读起来像是要检查记录中是否完全缺少该属性,但实际上它检查的是从Item 值推断出的相等性。 Key, Attr boto3.amazonaws.com/v1/documentation/api/latest/reference/… 的文档
    • “我发现 not_exists() 的名称令人困惑。” - 那是因为你不明白。它确实会检查属性是否存在,但它可用于检查匹配的分区键。如何?当您使用 put_item 添加具有与现有记录匹配的分区键值的记录时,请考虑一下这种情况。 DynamoDB 即将覆盖它(默认行为),但是如果它已经具有分区键(因为所有记录都必须具有分区键属性),那么您的 Condition 会说不。这是对相等性来源的现有记录的覆盖
    • 这种方法仅在 foo 和 bar 是哈希/范围键时有效。它不适用于常规属性。见forums.aws.amazon.com/thread.jspa?messageID=582356#582356
    • 用 AWS 的一个例子来支持这个答案:docs.aws.amazon.com/amazondynamodb/latest/developerguide/…
    【解决方案2】:

    您不需要排序键(或范围键),只需分区键或哈希键就足够了。

    try:
        table.put_item(
            Item={
                'foo':1,
                'bar':2,
            },
            ConditionExpression='attribute_not_exists(foo)'
        )
    except botocore.exceptions.ClientError as e:
        # Ignore the ConditionalCheckFailedException, bubble up
        # other exceptions.
        if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
            raise
    

    【讨论】:

    • 这是不正确的。该表有一个复合键,它需要散列键和范围(排序)键来唯一标识一条记录。如果存在共享相同 hash_key 的其他记录,这将使 ConditionExpression 失败,例如{ 'foo': 1, 'bar': 1}, { 'foo': 1, 'bar': 3}, { 'foo': 1, 'bar': 4} 这不是 OP 想要的,只有在 { 'foo': 1, 'bar': 3} 存在时才会失败。它可能适合其他用例,或者您的哈希键可能足以唯一标识记录,但如果是这种情况,那么您的范围键什么都不做,对一袋 1 进行排序是没有意义的。
    • 好的,我收回我试过了,这确实有效!对不起。我认为这是因为 ConditionExpression 旨在应用于属性,而不是键。要放置的记录由传入的Item 标识,当然任何一个 Key 属性都会根据定义始终存在。
    • 是的,没关系。甚至我一开始也很困惑..嘿只是出于好奇,为什么错误的答案会被赞成,而正确的答案却没有得到那种牵引力。为什么?
    • 堆栈溢出并非总是如此。大多数好的答案都会被赞成,但对于什么是好的答案却有不同的看法。有时答案在过去是正确的,但现在是错误的。有时人们只是喜欢一种风格而不是另一种风格。不好的是,当人们在没有就原因发表评论的情况下投了反对票。当人类参与时,没有完美的论坛,总是政治。大多数人都很好,这是一个很好的讨论:codeblog.jonskeet.uk/2018/03/17/stack-overflow-culture
    【解决方案3】:

    我认为使用 client.put_item 而不是 table.put_item 可以获得更好的文档

    来自boto3 documentation:

    要防止新项目替换现有项目,请使用包含attribute_not_exists 函数的条件表达式,并将属性名称用作表的分区键。由于每条记录都必须包含该属性,attribute_not_exists 函数只有在不存在匹配项时才会成功。

    条件表达式:

    ConditionExpression (string) -- 必须满足的条件 使有条件的 PutItem 操作成功。

    表达式可以包含以下任何内容:

    函数:attribute_exists |属性不存在 |属性类型 | 包含 | begin_with | size 这些函数名区分大小写。

    我在 item.save() 上使用 dynamodb2 overwrite parameter

    【讨论】:

    • 嗯...我已经阅读了文档。但是我发现写一个条件表达式是不直观的,我担心我写它的方式可能不会像我认为的那样做。所以想知道以前是否有人真的做过。你有一个例子说明你是如何做到的吗?我会更新我的答案,展示我是如何做到的
    • 我在答案中添加了我是如何做到的......(使用覆盖)
    • 啊好吧...那是使用 boto。我在问boto3。不过感谢您抽出宝贵时间来看看。
    • 通常在使用 boto3 时,建议在存在功能的情况下使用(更高级别的)resource API,如果没有,则回退到使用client API。在这种情况下 - 在这种情况下 table.put_item 绝对是更好的选择,因为您可以使用 from boto3.dynamodb.conditions import Key, Attr 并且这两件事使构建表达式更加容易。 resource 的文档更简单,因为选项较少,但 client 文档中所说的大部分内容也适用于 resource
    猜你喜欢
    • 2021-04-25
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-07
    相关资源
    最近更新 更多