2010-10-21 23 views
1

我有一個唯一的整數需要與問候的外鍵遞增模式,下面的代碼是我目前如何處理它:Django的 - 下拉列表AutoField至於外鍵

class MyModel(models.Model): 
    business = models.ForeignKey(Business) 
    number = models.PositiveIntegerField() 
    spam = models.CharField(max_length=255) 

    class Meta: 
     unique_together = (('number', 'business'),) 

    def save(self, *args, **kwargs): 
     if self.pk is None: # New instance's only 
      try: 
       highest_number = MyModel.objects.filter(business=self.business).order_by('-number').all()[0].number 
       self.number = highest_number + 1 
      except ObjectDoesNotExist: # First MyModel instance 
       self.number = 1 
     super(MyModel, self).save(*args, **kwargs) 

我對本以下問題:

  1. 多的人可以創造MyModel實例爲同一business,所有在互聯網上。是否有可能同時創建MyModel實例的2個人,並且.count()同時返回500個,然後都嘗試在同一時間基本上設置self.number = 501(引發IntegrityError)?答案似乎是一個明顯的「是的,它可能發生」,但我不得不問。
  2. 有沒有一個捷徑,或者「最好的方式」來做到這一點,我可以使用(或者可以用SuperAutoField來處理這個問題)?

我不能只巴掌while model_not_saved:try:except IntegrityError:的,因爲模型中的其他限制可能會導致一個無限循環和災難切爾諾貝利相比更糟糕的(也許不是那麼糟糕)。

回答

2

你想要在數據庫級別的約束。否則,你將最終遇到你所討論的併發問題。解決方案是將整個操作(讀取,增量,寫入)包裝在事務中。

爲什麼不能使用AutoField代替PositiveIntegerField?

number = models.AutoField() 

然而,在這種情況下,數量幾乎肯定會等於yourmodel.id,所以爲什麼不使用呢?

編輯:

哦,我明白你想要什麼。除非有多個MyModel.business實例,否則您需要一個不增加的數字字段。

如果可以的話,我仍然會推薦使用id字段,因爲它肯定是唯一的。如果你絕對不想這樣做(也許你向用戶顯示這個號碼),那麼你將需要在交易中包裝你的保存方法。

您可以在文檔閱讀更多關於交易:如果你只是用這個來算爲MyModel的有多少實例,一個FK商務

http://docs.djangoproject.com/en/dev/topics/db/transactions/

,你應該做的,作爲一個查詢而不是試圖存儲一個計數。

+0

@保羅 - 謝謝。我不明白。我不知道事務會保證在事務處理期間讀取的任何行沒有變化,當然,保護部分失敗的提交階段也是如此。這是如此嗎?如果是這樣,我可以簡單地保存在'@ commit_on_success'中,否? – orokusaki 2010-10-21 20:59:12

+0

@Paul - 似乎事務不會解決併發中發生的另一個破壞約束的'IntegrityError'問題,它可能與'number'引起的錯誤相混淆。 – orokusaki 2010-10-21 21:00:57

+0

精確的交易行爲將取決於您使用的數據庫以及配置方式。如果啓用事務中間件並將隔離級別設置爲可重複讀取數據庫,則您的方法將按原樣工作。在這種情況下,這可能不會成爲性能問題,但可能會影響其他代碼。 – 2010-10-21 21:12:49