Example of update_item in dynamodb boto3

后端 未结 5 745
陌清茗
陌清茗 2020-12-14 00:10

Following the documentation, I\'m trying to create an update statement that will update or add if not exists only one attribute in a dynamodb table.

I\'m trying this

相关标签:
5条回答
  • 2020-12-14 00:52

    Details on dynamodb updates using boto3 seem incredibly sparse online, so I'm hoping these alternative solutions are useful.

    get / put

    import boto3
    
    table = boto3.resource('dynamodb').Table('my_table')
    
    # get item
    response = table.get_item(Key={'pkey': 'asdf12345'})
    item = response['Item']
    
    # update
    item['status'] = 'complete'
    
    # put (idempotent)
    table.put_item(Item=item)
    

    actual update

    import boto3
    
    table = boto3.resource('dynamodb').Table('my_table')
    
    table.update_item(
        Key={'pkey': 'asdf12345'},
        AttributeUpdates={
            'status': 'complete',
        },
    )
    
    0 讨论(0)
  • 2020-12-14 00:52

    The original code example:

    response = table.update_item(
        Key={'ReleaseNumber': '1.0.179'},
        UpdateExpression='SET',
        ConditionExpression='Attr(\'ReleaseNumber\').eq(\'1.0.179\')',
        ExpressionAttributeNames={'attr1': 'val1'},
        ExpressionAttributeValues={'val1': 'false'}
    )
    

    Fixed:

    response = table.update_item(
        Key={'ReleaseNumber': '1.0.179'},
        UpdateExpression='SET #attr1 = :val1',
        ConditionExpression=Attr('ReleaseNumber').eq('1.0.179'),
        ExpressionAttributeNames={'#attr1': 'val1'},
        ExpressionAttributeValues={':val1': 'false'}
    )
    

    In the marked answer it was also revealed that there is a Range Key so that should also be included in the Key. The update_item method must seek to the exact record to be updated, there's no batch updates, and you can't update a range of values filtered to a condition to get to a single record. The ConditionExpression is there to be useful to make updates idempotent; i.e. don't update the value if it is already that value. It's not like a sql where clause.

    Regarding the specific error seen.

    ExpressionAttributeNames is a list of key placeholders for use in the UpdateExpression, useful if the key is a reserved word.

    From the docs, "An expression attribute name must begin with a #, and be followed by one or more alphanumeric characters". The error is because the code hasn't used an ExpressionAttributeName that starts with a # and also not used it in the UpdateExpression.

    ExpressionAttributeValues are placeholders for the values you want to update to, and they must start with :

    0 讨论(0)
  • 2020-12-14 00:53

    If you don't want to check parameter by parameter for the update I wrote a cool function that would return the needed parameters to perform a update_item method using boto3.

    def get_update_params(body):
        """Given a dictionary we generate an update expression and a dict of values
        to update a dynamodb table.
    
        Params:
            body (dict): Parameters to use for formatting.
    
        Returns:
            update expression, dict of values.
        """
        update_expression = ["set "]
        update_values = dict()
    
        for key, val in body.items():
            update_expression.append(f" {key} = :{key},")
            update_values[f":{key}"] = val
    
        return "".join(update_expression)[:-1], update_values
    

    Here is a quick example:

    def update(body):
        a, v = get_update_params(body)
        response = table.update_item(
            Key={'uuid':str(uuid)},
            UpdateExpression=a,
            ExpressionAttributeValues=dict(v)
            )
        return response
    
    0 讨论(0)
  • 2020-12-14 00:53

    Based on the official example, here's a simple and complete solution which could be used to manually update (not something I would recommend) a table used by a terraform S3 backend.

    Let's say this is the table data as shown by the AWS CLI:

    $ aws dynamodb scan --table-name terraform_lock --region us-east-1
    {
        "Items": [
            {
                "Digest": {
                    "S": "2f58b12ae16dfb5b037560a217ebd752"
                },
                "LockID": {
                    "S": "tf-aws.tfstate-md5"
                }
            }
        ],
        "Count": 1,
        "ScannedCount": 1,
        "ConsumedCapacity": null
    }
    

    You could update it to a new digest (say you rolled back the state) as follows:

    import boto3
    
    dynamodb = boto3.resource('dynamodb', 'us-east-1')
    
    
    try:
        table = dynamodb.Table('terraform_lock')
        response = table.update_item(
            Key={
                "LockID": "tf-aws.tfstate-md5"
            },
            UpdateExpression="set Digest=:newDigest",
            ExpressionAttributeValues={
                ":newDigest": "50a488ee9bac09a50340c02b33beb24b"
            },
            ReturnValues="UPDATED_NEW"
        )
    except Exception as msg:
        print(f"Oops, could not update: {msg}")
    

    Note the : at the start of ":newDigest": "50a488ee9bac09a50340c02b33beb24b" they're easy to miss or forget.

    0 讨论(0)
  • 2020-12-14 01:05

    Found working example here, very important to list as Keys all the indexes of the table, this will require additional query before update, but it works.

    response = table.update_item(
        Key={
            'ReleaseNumber': releaseNumber,
            'Timestamp': result[0]['Timestamp']
        },
        UpdateExpression="set Sanity = :r",
        ExpressionAttributeValues={
            ':r': 'false',
        },
        ReturnValues="UPDATED_NEW"
    )
    
    0 讨论(0)
提交回复
热议问题