DynamoDB GetItem en Python (boto3)
get_item recupera un elemento por su clave principal completa. El cliente de bajo nivel de boto3 (boto3.client("dynamodb")) habla JSON de DynamoDB en ambas direcciones, así que la clave entra envuelta con su tipo y el elemento vuelve igual. En qué se diferencia de query y scan está en acciones sobre elementos.
Código
import boto3
client = boto3.client("dynamodb")
response = client.get_item(
TableName="Music",
Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
ProjectionExpression="#proj0, #proj1, #proj2, #proj3",
ExpressionAttributeNames={"#proj0": "Artist", "#proj1": "SongTitle", "#proj2": "AlbumTitle", "#proj3": "Year"},
)
item = response.get("Item")
if item is None:
print("Item not found")
else:
print(item)Explicación
Un fallo devuelve una respuesta sin ninguna clave Item. Ni None, ni un diccionario vacío. Leyendo la misma tabla con una clave que no existe, las claves de nivel superior de la respuesta fueron exactamente:
['ResponseMetadata']Por eso el fragmento usa response.get("Item"). response["Item"] lanza KeyError en la ruta normal de «no encontrado», que es como una fila ausente se convierte en un 500 en un handler web. Aun así se te factura la lectura: la página de capacidad de lectura de AWS afirma que "if you perform a read operation on an item that doesn't exist, DynamoDB will still consume read throughput as outlined above" (consultada el 2026-07-28).
Year es una palabra reservada, y por eso el fragmento generado pone alias a cada atributo proyectado. Quita los alias #proj y pasa ProjectionExpression="Year" y el motor rechaza la lectura:
ValidationException: Invalid ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: YearPoner alias siempre no cuesta nada y elimina toda esa clase de fallo. La lista completa tiene 573 palabras; consulta "Attribute name is a reserved keyword".
Cuatro formas de equivocarte con la Key, tres mensajes distintos. Vale la pena distinguirlos, porque ninguno es el error «provided key element does not match the schema» que la gente espera. Reproducido contra una tabla Music con clave Artist (partición) + SongTitle (ordenación):
| Lo que pasaste | Mensaje literal de ValidationException |
|---|---|
{"Artist": …} — falta la clave de ordenación | The number of conditions on the keys is invalid |
{"Artist": …, "SongTitle": …, "Extra": …} | The number of conditions on the keys is invalid |
{"Artist": …, "Song": …} — nombre de atributo mal | One of the required keys was not given a value |
{"Artist": {"N": "1"}, …} — tipo equivocado | One or more parameter values were invalid: Type mismatch for key |
Fíjate en que un atributo de clave ausente y uno de más producen el mismo mensaje, así que «number of conditions» significa «no me diste exactamente el esquema de clave», no «pasaste demasiados pocos».
ProjectionExpression recorta la carga útil, no la factura. Leyendo un elemento de ~15 KB de tres formas con ReturnConsumedCapacity="TOTAL":
full item, eventually consistent CapacityUnits: 2.0
ProjectionExpression="#y" (Year only) CapacityUnits: 2.0
ProjectionExpression="#y" + ConsistentRead CapacityUnits: 4.0La proyección cambió la respuesta de ~15 KB a un solo número y no cambió el coste en nada. AWS lo dice sin rodeos: "The number of capacity units consumed will be the same whether you request all of the attributes (the default behavior) or just some of them (using a projection expression)" (API Reference de Query, consultada el 2026-07-28). ConsistentRead=True es el único flag de esa lista que mueve el número, y lo duplica. Consulta expresiones de proyección para saber para qué sirven realmente las proyecciones.
La API de recursos es un contrato distinto, no una forma más agradable de escribir lo mismo. boto3.resource("dynamodb").Table("Music").get_item(...) devuelve Python plano y cada número como decimal.Decimal:
{'Artist': 'Arturo Sandoval', 'AlbumTitle': 'Danzon', 'Awards': Decimal('0'), 'Year': Decimal('1994'), 'SongTitle': 'Cubano Chant'}Eso corta por los dos lados. Escribir de vuelta a través de la misma API con un float falla antes de que la petición salga de tu máquina:
TypeError: Float types are not supported. Use Decimal types instead.Si eso te muerde, "Float types are not supported" tiene la solución. Mezclar las dos API en una misma base de código es la trampa de verdad: el cliente de bajo nivel aceptará encantado un {"N": "1.5"} que la API de recursos habría rechazado.
Los errores llegan como excepciones de botocore, y boto3 les da clases reales. En la 1.43.58 el objeto lanzado por una condición fallida es ConditionalCheckFailedException, una subclase de ClientError, así que tanto except ClientError con una comprobación de err.response["Error"]["Code"] como except client.exceptions.ConditionalCheckFailedException funcionan. Prefiere el que ya use tu base de código; no compares con str(e).
Hazlo visualmente
Antes de poner alias a mano: el comprobador de palabras reservadas de DynamoDB gratuito toma tus nombres de atributo, te dice cuáles de las 573 palabras reservadas has tocado y emite el mapa ExpressionAttributeNames listo para pegar.
Para explorar tablas y ejecutar GetItem contra tus propios datos — forma de la clave, cuadrícula de resultados, copiar la petición de vuelta como boto3 — descarga DynoTable.
Guías relacionadas
- Query frente a Scan — cuándo un solo
get_itemgana a unquery. - Tipos de datos de DynamoDB — cómo se representa cada tipo de atributo en JSON de DynamoDB.
- DynamoDB ResourceNotFoundException — el primer error habitual aquí: nombre de tabla o región equivocados.
- "The provided key element does not match the schema" — la clave que pasas no coincide con el esquema de clave de la tabla.
Referencias
- GetItem — Amazon DynamoDB API Reference
- get_item — Boto3 DynamoDB.Client Reference
- Read consistency — Amazon DynamoDB Developer Guide
- Capacity unit consumption — Amazon DynamoDB Developer Guide
Reproducido el 2026-07-28 contra DynamoDB Local (amazon/dynamodb-local) en el puerto 9000 con boto3 1.43.58 / botocore 1.43.58. Todos los mensajes y cifras de capacidad de arriba son salida del motor, copiada literalmente. DynamoDB Local no es el servicio; donde se sabe que los dos redactan un error de forma distinta, lo decimos en la página del error.