DynamoDB UpdateItem en Python (boto3)

boto3 te da dos clientes para esta llamada y no se ponen de acuerdo sobre qué es un número. El client de bajo nivel de abajo envía y recibe JSON de DynamoDB, donde cada número es una cadena entrecomillada. resource("dynamodb").Table(...) acepta objetos nativos de Python, rechaza float de plano y te devuelve los números como decimal.Decimal. Elegir uno es la decisión de verdad en esta página.

Código

import boto3

client = boto3.client("dynamodb")

response = client.update_item(
    TableName="Music",
    Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
    UpdateExpression="SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2",
    ExpressionAttributeNames={"#upd0": "Genre", "#upd1": "Year", "#upd2": "Awards"},
    ExpressionAttributeValues={":updValue0": {"S": "Latin Jazz"}, ":updValue1": {"N": "1994"}, ":updValue2": {"N": "1"}},
    ReturnValues="ALL_NEW",
)

print(response["Attributes"])  # the item after the update

Explicación

  • La gramática de las cláusulas no es asunto de boto3. La UpdateExpression es una cadena opaca que reenvía; solo DynamoDB la analiza, así que los errores cuestan un viaje de ida y vuelta. ADD aquí es el incremento atómico que elimina la carrera de leer-modificar-escribir, attribute_exists(Artist) en una ConditionExpression convierte el upsert en solo-actualización, y el resto está en expresiones de actualización.
  • La respuesta tiene exactamente dos claves de nivel superior: Attributes y ResponseMetadata. No hay campo de estado que comprobar ni recuento de filas. Si la llamada volvió, funcionó; ResponseMetadata lleva el RequestId y el HTTPStatusCode que quieres en una línea de log.
  • ReturnValues="UPDATED_NEW" es la opción frugal. Devuelve solo los atributos que tocó la expresión, lo que en un elemento grande es la diferencia entre leer un contador y traerte el registro entero de vuelta.
  • Los errores llegan como botocore.exceptions.ClientError, y ramificas según e.response["Error"]["Code"]. Un alias que falta produce una ValidationException con el mensaje Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year. Las subclases tipadas sí existen, pero solo como atributos que botocore genera en la instancia del cliente (client.exceptions.ConditionalCheckFailedException), nunca como símbolos importables, así que una función auxiliar que no tenga el cliente a mano tiene que usar la cadena del código.

Decimal o JSON de DynamoDB, elige uno

La API de recursos rechaza float antes de construir la petición, con un mensaje que te dice exactamente lo que quiere:

TypeError: Float types are not supported. Use Decimal types instead.

Eso es la comprobación de tipos de boto3, no la de DynamoDB. Guarda Decimal("4.5") por la API de recursos y lee el mismo atributo de vuelta por los dos clientes, y obtienes:

resource Rating: Decimal('4.5') Awards: Decimal('2')
client Rating: {'N': '4.5'} Awards: {'N': '2'}

Ninguno está mal; son contratos distintos. Decimal conserva la precisión que DynamoDB realmente almacena y te obliga a pensar en la aritmética, a cambio de que Decimal("1") * 2 aparezca en código que esperaba un int. El cliente de bajo nivel te da cadenas y te deja el parseo a ti, que es lo que hace el fragmento de arriba.

La regla que se sigue de ahí: no los mezcles en una misma ruta de código. Un elemento escrito con Table.put_item y leído con client.get_item vuelve con otra forma, y el bug aparece en la rama que menos probaste.

Una nota sobre los atributos de TTL

El SET numérico más común en una base de código Python es un TTL: SET expires_at = :t con una época Unix. DynamoDB lee ese atributo en segundos. Escribe int(time.time() * 1000) en su lugar y el valor es 1785269450912, que como segundos cae en el año 58542, así que el elemento no se elimina nunca y nada se queja. El conversor de TTL de DynamoDB lee una época en las dos unidades y te dice cuál escribiste. Para leer después el valor almacenado desde una tabla real, descarga DynoTable.

Guías relacionadas

Referencias

Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.

Trabaja con DynamoDB sin la Consola

Un cliente de escritorio rápido para DynamoDB que ejecuta el SQL real que DynamoDB no puede — JOINs, GROUP BY, agregaciones — con edición visual y un agente de IA con tus propias claves de Bedrock.

Prueba gratuita de 30 días, sin tarjeta — después, el plan Free sin límite de tiempo.