2016-07-31 34 views
2

最近我發現Django ORM的Model.save()默認情況下執行SQL來更新'ALL'列,即使沒有任何修改。 這真的讓我擔心,因爲我所做的任何更改都有機會通過其他Model.save()進程恢復到原始值。Django的Model.save()修改不需要的列

例如,我有一個型號Order。有兩個併發進程(P1,P2)在其上運行。首先,P1選擇行:

# P1 
order = Order.objects.get(pk=10000) 

然後,P2選擇同一行,並更新status列:(下面的語句可以被包裝在一個事務,甚至是SERIALIZABLE之一,但這不能。解決這一問題)

# P2 
order = Order.objects.get(pk=10000) 
if order.status == UNPAID: 
    order.status = PAID # update the `status` column 
    order.save() 

之後,P1一些更新等瑣碎柱:

# P1 
order.xxx = xxx # update some other trivial column 
order.save() # This would set the `status` back to UNPAID !!! 

order.status將被重新設置爲UNPAID,那不是我想要的。

我知道我可以使用save(update_fields=...),select_for_update(),filter(...).update(...),SERIALIZABLE事務,或顯式鎖定P1以防止此問題。 但問題是:在整個項目的所有Model.save()報表中使用它們是很荒謬的。此外,即使我在我的項目代碼中執行,還有一些其他代碼正在執行此操作(Django Admin,ModelForm ...)。

我應該重寫Model.save()以只更新那些修改過的字段嗎?

(這似乎是一個嚴重的問題,對我來說,還是我採取了什麼了嗎?)

+0

它發生是因爲'P1'不知道你改變了'P2'的'status'屬性。爲什麼你需要2個不同的引用到相同的'訂單'? – DeepSpace

+0

@DeepSpace我想'P1'和'P2'是不同的過程。 – thebjorn

+0

@DeepSpace是的,它們是不同的過程。例如,工作人員正在使用Django Admin來修改該訂單('P1'),並且客戶正在進行付款('P2')。 – prajnamort

回答

0

至於其他人說的意見,你有經典型race condition。在Django中有一些處理方法。你已經提到了一些,但是根據我的經驗他們不能被認爲是安全的。我強烈建議你看this video,叫做Immutable Django,並開始朝這個方向挖掘。

基本上,您可以在模型中添加某種version列,並在每次保存時增加它並與現有的列進行比較。如果新版本相同或更少,則已保存一個 - 您試圖保存不實際的條目。

0

你只需要一個類似的競爭條件一個欄發生:

# Process 1 
account = Account.objects.get(pk=1) 
account.balance += 1000 

# Process 2 
account = Account.objects.get(pk=1) 
account.balance -= 10 
account.save() 

# Process 1 
account.save() 

現在從過程2中的餘額扣除丟失。您必須必須使用select_for_update以確保您的寫入是一致的。即使您只保存「髒」字段,並將其設爲默認字段,這仍然會發生。