Python(boto3)中的 DynamoDB UpdateItem
boto3 給了你兩個 client 來做這次呼叫,而它們對「數字是什麼」意見不合。下面這個低階 client 收發的是 DynamoDB JSON,其中每個數字都是加了引號的字串。resource("dynamodb").Table(...) 收的是原生 Python 物件,直接拒絕 float,並把數字以 decimal.Decimal 交還給你。挑哪一個,才是這一頁真正的決定。
程式碼
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說明
- 子句文法不關 boto3 的事。
UpdateExpression是一個它原封轉送的不透明字串;只有 DynamoDB 會去解析它,所以寫錯的代價是一次來回。這裡的ADD是消除讀取-修改-寫入競賽的不可分割遞增,在ConditionExpression裡放attribute_exists(Artist)會把 upsert 變成「只更新」,其餘的都在更新運算式裡。 - 回應剛好有兩個最上層的鍵 —
Attributes與ResponseMetadata。沒有狀態欄位可以檢查,也沒有列數。只要呼叫回來了,它就成功了;ResponseMetadata帶著你想寫進記錄行裡的RequestId與HTTPStatusCode。 ReturnValues="UPDATED_NEW"是省吃儉用的選項。它只回傳運算式碰過的屬性,在大型項目上,這就是「讀一個計數器」與「把整筆記錄運回來」之間的差別。- 錯誤是以
botocore.exceptions.ClientError抵達的,你依e.response["Error"]["Code"]分支。少了別名會產生ValidationException,訊息是Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year。型別化的子類別確實存在,但只以 botocore 在 client 實例上動態產生的屬性形式存在(client.exceptions.ConditionalCheckFailedException),絕不會是可匯入的符號,所以一個作用域裡沒有 client 的輔助函式,只能用代碼字串。
Decimal 或 DynamoDB JSON,挑一個
resource API 會在請求被組出來之前就拒絕 float,訊息還明確告訴你它要什麼:
TypeError: Float types are not supported. Use Decimal types instead.那是 boto3 自己的型別檢查,不是 DynamoDB 的。透過 resource API 存進 Decimal("4.5"),再用兩個 client 各讀回同一個屬性,你會得到:
resource Rating: Decimal('4.5') Awards: Decimal('2')
client Rating: {'N': '4.5'} Awards: {'N': '2'}兩者都沒錯;它們是不同的契約。Decimal 保住了 DynamoDB 實際儲存的精度,也逼你去想清楚算術,代價是 Decimal("1") * 2 會出現在原本預期看到 int 的程式碼裡。低階 client 給你字串,把解析留給你,也就是上面那段程式碼在做的事。
由此得出的規則是:不要在同一條程式碼路徑裡混用它們。一個透過 Table.put_item 寫入、再透過 client.get_item 讀取的項目,回來的形狀是不一樣的,而這個臭蟲會在你測得比較少的那一條分支上浮現。
關於 TTL 屬性的一點提醒
Python 程式碼庫裡最常見的數值 SET 就是 TTL:SET expires_at = :t 搭配一個 Unix epoch。DynamoDB 把那個屬性讀成秒。改寫成 int(time.time() * 1000),值就是 1785269450912,以秒來看會落在西元 58542 年,於是項目永遠不會被刪除,也沒有任何東西抱怨。DynamoDB TTL 轉換器會用兩種單位把 epoch 都讀一遍,並告訴你你寫的是哪一種。之後想從真實資料表把存進去的值讀回來,請下載 DynoTable。
相關指南
- DynamoDB 更新運算式 —
SET、ADD、REMOVE、DELETE與慣用寫法。 - 認識 ReturnValues — 每一個
ReturnValues選項會給你什麼。 - 「Attribute name is a reserved keyword」 — 為什麼這裡的別名對應表不是可選的。
- 「Invalid UpdateExpression」語法錯誤 — 常見 SET/ADD 語法錯誤的解讀。
參考資料
- UpdateItem — Amazon DynamoDB API Reference
- update_item — Boto3 DynamoDB.Client Reference
- Update expressions — Amazon DynamoDB Developer Guide
最後驗證於 2026-07-28,對照上方連結的 AWS 官方文件。