DynamoDB UpdateItem em Python (boto3)

O boto3 te dá dois clients para esta chamada e eles discordam sobre o que é um número. O client de baixo nível abaixo envia e recebe JSON do DynamoDB, onde todo número é uma string entre aspas. resource("dynamodb").Table(...) aceita objetos nativos do Python, recusa float de plano e devolve números como decimal.Decimal. Escolher um deles é a decisão de verdade desta 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

Explicação

  • A gramática das cláusulas não é assunto do boto3. A UpdateExpression é uma string opaca que ele repassa; só o DynamoDB a analisa, então erros custam uma ida e volta. ADD aqui é o incremento atômico que elimina a corrida de ler-modificar-escrever, attribute_exists(Artist) em uma ConditionExpression transforma o upsert em uma atualização apenas, e o resto está em expressões de atualização.
  • A resposta tem exatamente duas chaves de nível superior: Attributes e ResponseMetadata. Não há campo de status para verificar nem contagem de linhas. Se a chamada retornou, funcionou; ResponseMetadata carrega o RequestId e o HTTPStatusCode que você quer em uma linha de log.
  • ReturnValues="UPDATED_NEW" é a opção econômica. Ela retorna apenas os atributos que a expressão tocou, o que em um item grande é a diferença entre ler um contador e trazer o registro inteiro de volta.
  • Erros chegam como botocore.exceptions.ClientError, e você ramifica em e.response["Error"]["Code"]. Um alias faltando produz ValidationException com a mensagem Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year. As subclasses tipadas existem sim, mas apenas como atributos que o botocore gera na instância do client (client.exceptions.ConditionalCheckFailedException), nunca como símbolos importáveis, então uma função auxiliar sem o client no escopo tem que usar a string do código.

Decimal ou JSON do DynamoDB, escolha um

A API de resource rejeita float antes de a requisição ser montada, com uma mensagem que diz exatamente o que ela quer:

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

Isso é a checagem de tipos do próprio boto3, não do DynamoDB. Armazene Decimal("4.5") pela API de resource e leia o mesmo atributo de volta pelos dois clients, e você recebe:

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

Nenhum dos dois está errado; são contratos diferentes. Decimal mantém a precisão que o DynamoDB de fato armazena e te obriga a pensar sobre aritmética, ao custo de um Decimal("1") * 2 aparecer em código que esperava um int. O client de baixo nível te entrega strings e deixa a análise por sua conta, que é o que o trecho acima faz.

A regra que decorre disso: não misture os dois em um mesmo caminho de código. Um item gravado via Table.put_item e lido via client.get_item volta em um formato diferente, e o bug aparece justamente no ramo que você testou menos.

Uma observação sobre atributos de TTL

O SET numérico mais comum em uma base de código Python é um TTL: SET expires_at = :t com um epoch Unix. O DynamoDB lê esse atributo como segundos. Escreva int(time.time() * 1000) em vez disso e o valor é 1785269450912, que como segundos cai no ano 58542, então o item nunca é excluído e nada reclama. O conversor de TTL do DynamoDB lê um epoch de volta nas duas unidades e te diz qual delas você gravou. Para ler o valor armazenado de volta a partir de uma tabela real depois disso, baixe o DynoTable.

Guias relacionados

Referências

Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.

Trabalhe com o DynamoDB sem o Console

Um cliente desktop rápido para DynamoDB que roda o SQL de verdade que o DynamoDB não consegue — JOINs, GROUP BY, agregações — com edição visual e um agente de IA com suas próprias chaves do Bedrock.

Teste grátis de 30 dias, sem cartão de crédito — depois o plano Grátis sem limite de tempo.