2014-09-06 77 views
2

我有一個簡單的模型:如何避免與獨特的支票競爭條件在Django

class InvitationRequest(models.Model): 
    email = models.EmailField(max_length=255, unique=True) 

和一個簡單的模型形式:

class InvitationRequestForm(forms.ModelForm): 
    class Meta: 
     model = InvitationRequest 

現在,假設我嘗試處理它在一個標準的方式:

form = InvitationRequestForm(request.POST) 
if form.is_valid(): 
    form.save() 

有一種競爭狀態,因爲驗證執行一個簡單的SELECT查詢d確定這樣的電子郵件是否已經存儲,如果一切正常,那麼它繼續到form.save()行。如果有一個併發進程在同一時刻執行相同的進程,那麼這兩個表單都將進行驗證,並且這兩個進程將調用form.save(),因此一個將成功,另一個將失敗,從而導致IntegrityError

處理這個問題的標準方法是什麼?

我想在表單對象中有一個標準錯誤,所以我可以將它傳遞給模板並通知用戶有關該問題。

我知道:

  • 我/包一切與嘗試,除了增加新的錯誤,以我的形式手動
  • 我可以SERIALIZABLE交易(在MySQL包一切,因爲它執行下一鍵鎖定FO每一個選擇則)
  • 我可以使用覆蓋Model._perform_unique_checks,使 用它select_for_update(與MySQL的作品,因爲下一個鍵鎖定)
  • 我可以獲取表級排它鎖

這些解決方案都不具吸引力,我也使用PostgreSQL,它與MySQL不同。

+0

我不是Django的大用戶,但也許這樣的事情會幫助:http ://stackoverflow.com/q/20682954/1270148 – 2014-09-06 18:14:13

回答

6

的標準方法是不處理這個問題,如:

  1. 在你的情況下,失敗的概率接近於0;
  2. 失敗的嚴重程度非常低。

如果由於某種原因,您必須確定問題不會發生,那麼您就是您自己。

我還沒有詳細分析了事件發生的順序,但我覺得用SERIALIZABLE隔離級別不會真正的幫助,只會造成IntegrityError(或DatabaseError),以在不同的地方得到提升。

覆蓋Model._perform_unique_checks聽起來對我來說是一個壞主意,如果可能的話,你最好遠離猴子補丁(在這裏它是可能的)。

至於使用表鎖來防止不太可能的錯誤...嗯,我不是一個很大的粉絲,所以我不能推薦。

下面是對類似問題的一個很好的答案:https://stackoverflow.com/a/3523439/176186 - 我同意捕獲IntegrityError並且重試可能是解決該問題的最簡單最普通的方式。

編輯:我發現這個:Symfony2 - how to recover from unique constraint error after form submission?,我同意@ pid的答案。

2

我同意托馬斯·傑林斯基的看法,通常的做法是不擔心這一點。對於大多數使用情況來說,這是不值得的麻煩。

如果它重要,最好的方法可能是樂觀併發。在這種情況下,它可能看起來像(未經測試):

from django.forms.util import ErrorList 

def handle_form(request) 
    form = InvitationRequestForm(request.POST) 
    try: 
     if form.is_valid(): 
      form.save() 
      return HttpResponseRedirect(...) # redirect to success url 
    except IntegrityError: 
     form._errors['email'] = ErrorList() 
     form._errors['email'].append('Error msg') 

    return render(...) # re-render the form with errors 

SERIALIZABLE不會真正幫助在這裏。正如PostgreSQL documentation明確指出的那樣,您必須準備好處理序列化失敗,這意味着代碼看起來幾乎與上面相同。 (它幫助,但如果你沒有unique約束迫使數據庫拋出異常。)

+1

'new ErrorList()'絕對不是正確的:) :) – michael 2014-09-16 02:48:07

+2

@michael:哈,好抓。最近太多的Javascript ...編輯。 – 2014-09-16 03:08:48