DynamoDB Conditional Write in Python (boto3)

A conditional write attaches a ConditionExpression to a put_item, update_item, or delete_item: DynamoDB evaluates it against the current item and applies the write only if it holds — atomically, with no read-modify-write race. It's DynamoDB's optimistic-locking primitive. This example guards an update with a version check.

Code

import boto3

client = boto3.client("dynamodb")

# Update the item only if nobody changed it since we read version 7.
try:
    client.update_item(
        TableName="Music",
        Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
        UpdateExpression="SET #upd0 = :updValue0, #version = :newVersion",
        ConditionExpression="attribute_exists(#cond0) AND #version = :expectedVersion",
        ExpressionAttributeNames={"#upd0": "Genre", "#version": "Version", "#cond0": "Artist"},
        ExpressionAttributeValues={
            ":updValue0": {"S": "Latin Jazz"},
            ":expectedVersion": {"N": "7"},
            ":newVersion": {"N": "8"},
        },
        ReturnValuesOnConditionCheckFailure="ALL_OLD",
    )
    print("Updated to version 8")
except client.exceptions.ConditionalCheckFailedException as e:
    # With ReturnValuesOnConditionCheckFailure="ALL_OLD", the current item
    # rides back on the exception — no extra read to see what beat you.
    print("Lost the race — item is now:", e.response.get("Item"))

Explanation

  • ConditionExpression — checked against the stored item atomically with the write. Available functions: attribute_exists, attribute_not_exists, attribute_type, contains, begins_with, size, plus comparators (=, <>, <, >, <=, >=, BETWEEN, IN) and AND/OR/NOT.
  • The two workhorse idioms:
    • attribute_exists(#key) on an update = "update-only, never create". Without it, update_item on a missing key silently creates the item (the accidental-upsert bug).
    • attribute_not_exists(#key) on a put = "create-only, never overwrite" — shown on the PutItem page.
  • Optimistic locking — read the item (version 7), then write with #version = :expectedVersion while bumping to 8. Two concurrent writers can't both win; the loser gets ConditionalCheckFailedException, re-reads, and retries on the new version.
  • ReturnValuesOnConditionCheckFailure="ALL_OLD" — puts the current item on the exception response (e.response), saving the follow-up get_item after a failed check. It consumes no read capacity; a failed conditional write still bills its write attempt.
  • On the resource API the same guard is table.update_item(..., ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists()) with native Python values.

Do it visually

Condition expressions are exactly what the DynamoDB Expression Builder builds — pick the function, get the expression + name/value maps as runnable boto3 code.

To edit items with generated, reviewable expressions instead of hand-typed placeholders — download DynoTable.

References

Last verified 2026-07-13 against the official AWS documentation linked above.

Work with DynamoDB without the Console

DynoTable is a fast desktop client for DynamoDB — browse tables, run SQL-style queries, and edit items locally.