Reputation: 25569
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.
Upvotes: 43
Views: 68200
Reputation: 4855
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
Upvotes: 0
Reputation: 369
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
Upvotes: 14
Reputation: 1661
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.
Upvotes: 93
Reputation: 10056
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()
Upvotes: 3