DynamoDB GetItem in Python (boto3)

get_item recupera un Item dalla sua chiave primaria completa. Il client di basso livello di boto3 (boto3.client("dynamodb")) parla JSON DynamoDB in entrambe le direzioni, quindi la chiave entra avvolta nel suo tipo e l'Item torna indietro nello stesso modo. In cosa differisce da query e scan è trattato in azioni basate sugli Item.

Codice

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)

Spiegazione

Un mancato riscontro restituisce una risposta senza alcuna chiave Item. Non None, non un dizionario vuoto. Leggendo la stessa tabella con una chiave che non esiste, le chiavi di primo livello della risposta erano esattamente:

['ResponseMetadata']

Ecco perché lo snippet usa response.get("Item"). response["Item"] solleva KeyError sul normale percorso «non trovato», ed è così che una riga mancante si trasforma in un 500 dentro un handler web. La lettura ti viene fatturata comunque: la pagina AWS sulla capacità di lettura afferma che "if you perform a read operation on an item that doesn't exist, DynamoDB will still consume read throughput as outlined above" (recuperata il 2026-07-28).

Year è una parola riservata, ed è il motivo per cui lo snippet generato fa l'alias di ogni attributo proiettato. Togli gli alias #proj e passa ProjectionExpression="Year" e il motore rifiuta la lettura:

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

Fare l'alias in modo incondizionato non costa nulla e rimuove l'intera classe di errore. La lista completa è lunga 573 parole; vedi "Attribute name is a reserved keyword".

Quattro modi di sbagliare la Key, tre messaggi diversi. Vale la pena saperli distinguere, perché nessuno di essi è l'errore «provided key element does not match the schema» che ci si aspetta. Riprodotto su una tabella Music con chiave Artist (partizione) + SongTitle (ordinamento):

Cosa hai passatoMessaggio ValidationException alla lettera
{"Artist": …} — chiave di ordinamento mancanteThe number of conditions on the keys is invalid
{"Artist": …, "SongTitle": …, "Extra": …}The number of conditions on the keys is invalid
{"Artist": …, "Song": …} — nome di attributo erratoOne of the required keys was not given a value
{"Artist": {"N": "1"}, …} — tipo erratoOne or more parameter values were invalid: Type mismatch for key

Nota che un attributo di chiave mancante e uno in più producono lo stesso messaggio, quindi «number of conditions» significa «non mi hai passato esattamente lo schema di chiave», non «ne hai passati troppo pochi».

ProjectionExpression taglia il payload, non il conto. Leggendo un Item da ~15 KB in tre modi con ReturnConsumedCapacity="TOTAL":

full item, eventually consistent          CapacityUnits: 2.0
ProjectionExpression="#y" (Year only)     CapacityUnits: 2.0
ProjectionExpression="#y" + ConsistentRead  CapacityUnits: 4.0

La proiezione ha ridotto la risposta da ~15 KB a un singolo numero e ha cambiato il costo di nulla. AWS lo dice chiaramente: "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)" (Query API Reference, recuperata il 2026-07-28). ConsistentRead=True è l'unico flag di quell'elenco che muove il numero, e lo raddoppia. Vedi projection expression per capire a cosa servono davvero le proiezioni.

L'API resource è un contratto diverso, non una grafia più carina. boto3.resource("dynamodb").Table("Music").get_item(...) restituisce Python semplice e ogni numero come decimal.Decimal:

{'Artist': 'Arturo Sandoval', 'AlbumTitle': 'Danzon', 'Awards': Decimal('0'), 'Year': Decimal('1994'), 'SongTitle': 'Cubano Chant'}

Il coltello taglia da entrambe le parti. Riscrivere attraverso la stessa API con un float solleva un errore prima ancora che la richiesta lasci la tua macchina:

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

Se ti capita, "Float types are not supported" ha la soluzione. Mescolare le due API nello stesso codebase è la vera trappola: il client di basso livello accetterà senza obiezioni un {"N": "1.5"} che l'API resource avrebbe rifiutato.

Gli errori arrivano come eccezioni botocore, e boto3 dà loro classi vere. Sulla 1.43.58 l'oggetto sollevato per una condizione fallita è ConditionalCheckFailedException, una sottoclasse di ClientError, quindi funzionano sia except ClientError più un controllo su err.response["Error"]["Code"], sia except client.exceptions.ConditionalCheckFailedException. Preferisci quello che il tuo codebase usa già; non fare match su str(e).

Fallo visivamente

Prima di fare gli alias a mano: il verificatore delle parole riservate DynamoDB gratuito prende i tuoi nomi di attributo, ti dice quali delle 573 parole riservate hai colpito ed emette la mappa ExpressionAttributeNames pronta da incollare.

Per sfogliare le tabelle ed eseguire GetItem sui tuoi dati — form della chiave, griglia dei risultati, richiesta ricopiabile in boto3 — scarica DynoTable.

Guide correlate

Riferimenti

Riprodotto il 2026-07-28 su DynamoDB Local (amazon/dynamodb-local) sulla porta 9000 con boto3 1.43.58 / botocore 1.43.58. Ogni messaggio e ogni cifra di capacità qui sopra è output del motore, copiato alla lettera. DynamoDB Local non è il servizio; dove è noto che i due formulino un errore in modo diverso lo segnaliamo sulla pagina dell'errore.

Lavora con DynamoDB senza la Console

Un client desktop veloce per DynamoDB che esegue il vero SQL che DynamoDB non può — JOINs, GROUP BY, aggregazioni — con modifica visuale e un agente AI sulle tue chiavi Bedrock.

Prova gratuita di 30 giorni, senza carta di credito — poi il piano Free senza limiti di tempo.