How do I conditionally insert an item into a dynamodb table using boto3

Question:

If I have a table with a hash key of userId and a range key of productId how do I put an item into that table only if it doesn’t already exist using boto3’s dynamodb bindings?

The normal call to put_item looks like this

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

My call with a ConditionExpression looks like this:

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

But this raises a ConditionalCheckFailedException every time. Whether an item exists with the same productId or not.

Asked By: aychedee

||

Answers:

i think that you will get better documentation using client.put_item rather than table.put_item

from boto3 documentation:

To prevent a new item from replacing an existing item, use a conditional expression that contains the attribute_not_exists function with the name of the attribute being used as the partition key for the table. Since every record must contain that attribute, the attribute_not_exists function will only succeed if no matching item exists.

ConditionExpression:

ConditionExpression (string) — A condition that must be satisfied in
order for a conditional PutItem operation to succeed.

An expression can contain any of the following:

Functions: attribute_exists | attribute_not_exists | attribute_type |
contains | begins_with | size These function names are case-sensitive.

i am using dynamodb2 overwrite parameter on item.save()

Answered By: Eyal Ch

The documentation for this unfortunately isn’t super clear. I needed to accomplish something similar, and here’s what worked for me, using 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

Similar to the other answer, the key is in the attribute_not_exists function, but it was unclear to me initially how to get that to work. After some experimentation, I was able to get it going with the above.

Answered By: jimjkelly

You dont need the sortkey( or range key) just the partition key or hash key is enough.

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
Answered By: Venkat.V.S

To prevent a new item from replacing an existing item, use a conditional expression that contains the attribute_not_exists function with the name of the attribute being used as the partition key for the table.

You can use the errorfactory which will create an error class based on the codes returned.

import boto3

res = boto3.resource('dynamodb')
table = res.Table('mytable') 

try: 
    conditionalUpdateResponse = table.put_item(
        Item={'userId': 1, 'productId': 2},
        ConditionExpression='attribute_not_exists(userId)',
    )
except res.meta.client.exceptions.ConditionalCheckFailedException as e: 
    print(e)

Source: https://github.com/boto/boto3/issues/2235#issuecomment-574929483

AWS Boto3 docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/put_item.html

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.