Écriture conditionnelle DynamoDB en Python (boto3)

boto3 est le seul SDK où une écriture conditionnelle dispose d'une classe d'exception nommée à attraper, et c'est aussi le seul où l'élément renvoyé se cache à un endroit que tu ne devinerais pas. L'expression elle-même fonctionne pareil partout ; les expressions de condition DynamoDB couvrent les fonctions et le motif du verrouillage optimiste.

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"))

Explication

  • ConditionalCheckFailedException est une classe modélisée, donc except client.exceptions.… fonctionne. La plupart des erreurs DynamoDB ne le sont pas : ValidationException n'a aucune classe et doit être discriminée sur e.response["Error"]["Code"]. La classe modélisée hérite quand même de ClientError, donc un except ClientError large placé en amont l'avalera si tu ordonnes tes handlers sans précaution.
  • L'élément renvoyé est une clé de premier niveau de e.response, pas de e.response["Error"]. C'est pour ça que le bloc lit e.response.get("Item"). Il est facile d'aller chercher sous ["Error"], à côté de Code et Message, de ne rien trouver et d'en conclure que le paramètre n'a pas marché.
  • L'élément revient en DynamoDB JSON même si tu as l'habitude des valeurs natives, parce qu'il s'agit ici du client bas niveau. boto3.dynamodb.types.TypeDeserializer le convertit si tu veux du Python ordinaire.
  • L'API resource exprime la même garde sous forme d'objets, ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists(), avec des valeurs natives et sans maps de placeholders. Elle lève exactement la même exception, donc la gestion ci-dessous est inchangée.
  • Une vérification échouée facture quand même une écriture. Le Developer Guide est explicite : une condition fausse consomme de la capacité d'écriture, dimensionnée sur le plus gros de l'ancien et du nouvel élément. Une reprise sans plafond sur une clé disputée coûte donc de l'argent réel sans jamais avancer.

Où boto3 range l'élément renvoyé

Lance le bloc sur une Version stockée à 9 et affiche les clés de la réponse portée par l'exception. DynamoDB Local 3.3.0, boto3 1.43.58 :

sorted(e.response.keys())  ->  ['Error', 'Item', 'ResponseMetadata']

e.response["Item"]  ->  {'Artist': {'S': 'Arturo Sandoval'},
                         'Year': {'N': '1994'},
                         'Version': {'N': '9'},
                         'SongTitle': {'S': 'Cubano Chant'},
                         'AlbumTitle': {'S': 'Danzon'}}

Supprime ReturnValuesOnConditionCheckFailure et le même échec donne ['Error', 'ResponseMetadata']. La clé Item est absente, et e.response.get("Item") renvoie None au lieu de lever une exception. C'est la version de ce bug qui survit à la revue de code et se met à journaliser None en production.

Pourquoi chaque nom de l'expression est aliasé

Le bloc écrit #version et #cond0 plutôt que Version et Artist, ce qui paraît excessif pour deux mots ordinaires. Ça l'est, pour ces deux-là. Version n'est pas un mot réservé DynamoDB, et écrit en clair il passe la validation des noms.

Year est réservé, et la même table en a un. Garde dessus directement et tu obtiens :

ValidationException: Invalid ConditionExpression: Attribute name is a reserved keyword;
reserved keyword: Year

573 mots figurent sur cette liste, dont Name, Status, Size, Count, Data, Owner, Timestamp et Items. Tout aliaser, c'est ainsi que le code généré évite d'avoir à savoir lequel est lequel. Colle tes noms d'attributs dans le vérificateur de mots réservés et il te rend la map ExpressionAttributeNames pour ceux qui en ont besoin.

Pour écrire ces gardes sur tes propres tables avec l'aliasage pris en charge pour toi, télécharge DynoTable.

Exemples liés

Références

Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.

Travaille avec DynamoDB sans la Console

Un client de bureau rapide pour DynamoDB qui exécute le vrai SQL que DynamoDB ne peut pas — JOINs, GROUP BY, agrégations — avec édition visuelle et un agent IA sur tes propres clés Bedrock.

Essai gratuit de 30 jours, sans carte bancaire — ensuite la formule Gratuit, sans limite de durée.