Escritura condicional en DynamoDB en Python (boto3)
boto3 es el único SDK donde una escritura condicional tiene una clase de excepción con nombre que capturar, y también es el único donde el Item devuelto se esconde en un sitio que no adivinarías. La expresión en sí funciona igual en todas partes; las expresiones de condición de DynamoDB cubren las funciones y el patrón de bloqueo optimista.
Código
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"))Explicación
ConditionalCheckFailedExceptiones una clase modelada, así queexcept client.exceptions.…funciona. La mayoría de los errores de DynamoDB no lo son:ValidationExceptionno tiene clase alguna y hay que emparejarlo cone.response["Error"]["Code"]. La clase modelada sigue heredando deClientError, así que unexcept ClientErroramplio más arriba se la tragará si ordenas tus manejadores sin cuidado.- El Item devuelto es una clave de primer nivel de
e.response, no dee.response["Error"]. Por eso el bloque leee.response.get("Item"). Es fácil buscarlo bajo["Error"]junto aCodeyMessage, no encontrar nada y concluir que el parámetro no funcionó. - El Item vuelve en DynamoDB JSON aunque estés acostumbrado a valores nativos, porque este es el cliente de bajo nivel.
boto3.dynamodb.types.TypeDeserializerlo convierte si quieres Python plano. - La API de recursos expresa la misma guarda con objetos,
ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists(), con valores nativos y sin mapas de marcadores. Lanza la misma excepción, así que el manejo de abajo no cambia. - Una comprobación fallida también factura una escritura. La Developer Guide es explícita en que una condición falsa consume capacidad de escritura, dimensionada según el mayor entre el Item antiguo y el nuevo, así que un reintento sin límite sobre una clave disputada cuesta dinero real mientras no avanza.
Dónde pone boto3 el Item devuelto
Ejecuta el bloque contra una Version almacenada de 9 e imprime las claves de la respuesta de la excepción. 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'}}Quita ReturnValuesOnConditionCheckFailure y el mismo fallo da ['Error', 'ResponseMetadata']. La clave Item no está, y e.response.get("Item") devuelve None en vez de lanzar. Esa es la versión de este bug que sobrevive a la revisión de código y empieza a registrar None en producción.
Por qué cada nombre de la expresión lleva alias
El bloque escribe #version y #cond0 en vez de Version y Artist, lo que parece excesivo para dos palabras corrientes. Lo es, para estas dos. Version no es una palabra reservada de DynamoDB y, usada tal cual, pasa la validación de nombres.
Year sí es reservada, y la misma tabla tiene una. Usa una guarda directamente sobre ella y obtienes:
ValidationException: Invalid ConditionExpression: Attribute name is a reserved keyword;
reserved keyword: YearHay 573 palabras en esa lista, incluidas Name, Status, Size, Count, Data, Owner, Timestamp e Items. Poner alias a todo es la forma en que el código generado evita tener que saber cuál es cuál. Pega tus nombres de atributo en el comprobador de palabras reservadas y te devuelve el mapa ExpressionAttributeNames para los que lo necesiten.
Para escribir estas guardas contra tus propias tablas con los alias resueltos por ti, descarga DynoTable.
Ejemplos relacionados
- Escritura condicional en DynamoDB en Node.js — el mismo bloqueo optimista con el SDK de AWS v3.
- Escritura condicional en DynamoDB con la AWS CLI — el mismo bloqueo optimista desde la shell.
- DynamoDB PutItem en Python — el put de solo creación con
attribute_not_exists. - Expresiones de condición de DynamoDB — todas las funciones, con patrones.
- Imponer unicidad sobre varios atributos — condiciones y transacciones combinadas.
- DynamoDB ConditionalCheckFailedException — cuándo la comprobación fallida es esperada y cómo manejarla barato.
Referencias
- UpdateItem — Amazon DynamoDB API Reference
- DynamoDB.Client.update_item — Boto3 documentation
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
- Reserved words in DynamoDB — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.