DynamoDB PutItem em Python (boto3)
put_item grava um item inteiro e substitui qualquer item existente com a mesma chave primária (ações baseadas em item cobre como isso difere de update_item). Com o client de baixo nível, cada atributo é passado como JSON do DynamoDB, e o boto3 confere esse formato localmente antes de qualquer coisa ser enviada.
Código
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:
raiseExplicação
{"N": 1994} nunca chega à AWS, e except ClientError não vai capturá-lo. O botocore valida a requisição contra o seu próprio modelo de serviço primeiro, e um int do Python onde o tipo N quer uma string falha ali:
ParamValidationError: Parameter validation failed:
Invalid type for parameter Item.Year.N, value: 1994, type: <class 'int'>, valid types: <class 'str'>ParamValidationError descende de BotoCoreError, não de ClientError, então o handler do trecho acima o deixa passar. Normalmente é isso que você quer, já que é um bug e não um resultado de negócio, mas significa que um try/except ClientError em volta de uma escrita não é um pega-tudo. O lado bom é que o erro nomeia o caminho exato, Item.Year.N, o que é melhor para depurar do que um ValidationException do lado do servidor. Mais sobre ele em "Parameter validation failed".
A superfície completa de uma condição malsucedida. Capturar o mesmo put condicional duas vezes e imprimir tudo o que há na exceção deu:
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 failedDuas coisas decorrem disso. No botocore 1.43.58 o objeto é uma subclasse modelada, então except client.exceptions.ConditionalCheckFailedException funciona junto com a verificação de err.response["Error"]["Code"] que o trecho usa; escolha uma e seja consistente. E str(e) é uma frase formatada, não a mensagem do serviço, então nunca a compare com um literal.
Uma condição malsucedida ainda cobra uma escrita. A AWS: "if the expression evaluates to false, DynamoDB still consumes write capacity units from the table" (consultada em 2026-07-28). Um loop de retry de criar-somente paga por cada tentativa rejeitada. Para dar escala, um put bem-sucedido de um item de ~15 KB reportou "CapacityUnits": 15 sob ReturnConsumedCapacity="TOTAL"; escritas arredondam para cima a cada 1 KB, e não os 4 KB que as leituras usam.
A API de resource é um contrato diferente, e float é onde você descobre isso. boto3.resource("dynamodb").Table("Music").put_item(Item={...}) aceita Python puro e faz o marshalling por você, mas recusa ponto flutuante binário de plano:
TypeError: Float types are not supported. Use Decimal types instead.Envolva o valor em decimal.Decimal("4.5"), a partir de uma string e não de um float, ou a imprecisão já está embutida antes de o Decimal vê-la. Ler de volta pela mesma API retorna todo número como Decimal, o que é uma mudança real no seu código, não um detalhe de formatação. Veja "Float types are not supported".
Misturar as duas APIs é a armadilha sobre a qual nenhuma delas avisa. O client de baixo nível aceita alegremente {"N": "1.5"}, um valor que a API de resource teria rejeitado como float. Uma base de código que grava com uma e lê com a outra recebe Decimal de volta de dados que nunca passaram por Decimal na entrada.
Os aliases #cond0 não são cosméticos. Eles resolvem para Artist/SongTitle via ExpressionAttributeNames. Nomes de atributo inline funcionam até o momento em que um colide com uma palavra reservada, e então a expressão falha em um nome que você não mudou.
Faça isso visualmente
Expressões de condição são onde escrever à mão dá errado primeiro, porque uma errada falha como uma escrita rejeitada e não como um erro de sintaxe. O DynamoDB Expression Builder gratuito monta a ConditionExpression com os seus mapas de nomes e valores e emite a chamada boto3 pronta para colar.
Para gravar e editar itens contra as suas próprias tabelas — um formulário por atributo, seletores de tipo, copiar o resultado de volta como boto3 — baixe o DynoTable.
Guias relacionados
- Expressões de condição do DynamoDB —
attribute_not_exists, bloqueio otimista e mais. - Tipos de dados do DynamoDB — como cada tipo de atributo é escrito em JSON do DynamoDB.
- DynamoDB ConditionalCheckFailedException — o que a condição de criar-somente lança quando o item já existe.
- DynamoDB ValidationException — o pega-tudo para um item ou expressão malformados.
Referências
- PutItem — Amazon DynamoDB API Reference
- put_item — Boto3 DynamoDB.Client Reference
- Error handling — Boto3 Developer Guide
- Capacity unit consumption — Amazon DynamoDB Developer Guide
- Condition expressions — Amazon DynamoDB Developer Guide
Reproduzido em 2026-07-28 com boto3 1.43.58 / botocore 1.43.58 contra o DynamoDB Local (amazon/dynamodb-local) na porta 9000. O texto da exceção, os campos da resposta e a leitura de capacidade são saída capturada, copiada literalmente.