DynamoDB PutItem in Python (boto3)

put_item scrive un Item intero e sostituisce qualsiasi Item esistente con la stessa chiave primaria (le azioni basate sugli Item spiegano in cosa differisce da update_item). Con il client di basso livello ogni attributo viene passato come JSON DynamoDB, e boto3 controlla quella forma in locale prima che venga inviato qualsiasi cosa.

Codice

import boto3
from botocore.exceptions import ClientError

client = boto3.client("dynamodb")

try:
    client.put_item(
        TableName="Music",
        Item={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}, "AlbumTitle": {"S": "Danzon"}, "Year": {"N": "1994"}, "Awards": {"N": "0"}},
        ConditionExpression="attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)",
        ExpressionAttributeNames={"#cond0": "Artist", "#cond1": "SongTitle"},
    )
    print("Song written")
except ClientError as err:
    if err.response["Error"]["Code"] == "ConditionalCheckFailedException":
        print("A song with that key already exists — not overwritten")
    else:
        raise

Spiegazione

{"N": 1994} non arriva mai ad AWS, e except ClientError non lo intercetterà. Botocore convalida prima la richiesta contro il proprio modello di servizio, e un int Python dove il tipo N vuole una stringa fallisce lì:

ParamValidationError: Parameter validation failed:
Invalid type for parameter Item.Year.N, value: 1994, type: <class 'int'>, valid types: <class 'str'>

ParamValidationError discende da BotoCoreError, non da ClientError, quindi l'handler nello snippet qui sopra la lascia passare. Di solito è quello che vuoi, dato che è un bug e non un esito di business, ma significa che un try/except ClientError attorno a una scrittura non è un catch-all. Il lato positivo è che l'errore nomina il percorso esatto, Item.Year.N, il che per il debug batte una ValidationException lato server. Approfondimento in "Parameter validation failed".

La superficie completa di una condizione fallita. Intercettando due volte lo stesso put condizionale e stampando tutto quello che c'è sull'eccezione:

type(e).__name__                              ConditionalCheckFailedException
e.response["Error"]["Code"]                   ConditionalCheckFailedException
e.response["Error"]["Message"]                The conditional request failed
e.response["ResponseMetadata"]["HTTPStatusCode"]  400
str(e)                                        An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed

Ne seguono due cose. Su botocore 1.43.58 l'oggetto è una sottoclasse modellata, quindi except client.exceptions.ConditionalCheckFailedException funziona accanto al controllo su err.response["Error"]["Code"] che usa lo snippet; scegline uno e sii coerente. E str(e) è una frase formattata, non il messaggio del servizio, quindi non confrontarla mai con una stringa letterale.

Una condizione fallita fattura comunque una scrittura. AWS: "if the expression evaluates to false, DynamoDB still consumes write capacity units from the table" (recuperato il 2026-07-28). Un loop di retry create-only paga per ogni tentativo rifiutato. Per dare la scala, un put riuscito di un Item da ~15 KB ha riportato "CapacityUnits": 15 con ReturnConsumedCapacity="TOTAL"; le scritture arrotondano per 1 KB, non per i 4 KB che usano le letture.

L'API resource è un contratto diverso, e float è il punto in cui te ne accorgi. boto3.resource("dynamodb").Table("Music").put_item(Item={...}) accetta Python semplice e fa il marshalling al posto tuo, ma rifiuta di netto la virgola mobile binaria:

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

Avvolgi il valore in decimal.Decimal("4.5"), partendo da una stringa e non da un float, altrimenti l'imprecisione è già dentro prima che Decimal la veda. Rileggere attraverso la stessa API restituisce ogni numero come Decimal, il che è un cambiamento reale nel tuo codice, non un dettaglio di formattazione. Vedi "Float types are not supported".

Mescolare le due API è la trappola di cui nessuna delle due avvisa. Il client di basso livello accetta senza obiezioni {"N": "1.5"}, un valore che l'API resource avrebbe rifiutato come float. Un codebase che scrive con una e legge con l'altra si ritrova Decimal a partire da dati che non sono mai passati per Decimal all'andata.

Gli alias #cond0 non sono cosmetici. Si risolvono in Artist/SongTitle attraverso ExpressionAttributeNames. I nomi di attributo inline funzionano finché uno non collide con una parola riservata, e a quel punto l'espressione fallisce su un nome che non hai toccato.

Fallo visivamente

Le condition expression sono il punto in cui la scrittura a mano sbaglia per prima, perché una condizione sbagliata fallisce come scrittura rifiutata e non come errore di sintassi. Il DynamoDB Expression Builder gratuito assembla la ConditionExpression con le sue mappe di nomi e valori ed emette la chiamata boto3 pronta da incollare.

Per scrivere e modificare Item sulle tue tabelle — un form per attributo, selettori di tipo, risultato ricopiabile in boto3 — scarica DynoTable.

Guide correlate

Riferimenti

Riprodotto il 2026-07-28 con boto3 1.43.58 / botocore 1.43.58 su DynamoDB Local (amazon/dynamodb-local) sulla porta 9000. Il testo dell'eccezione, i campi della risposta e la lettura di capacità sono output catturato, copiato alla lettera.

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.