2010-10-05 34 views
7

使用下面的代碼:錯誤使用基類領域的子類unique_together元選項

class Organization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    ... 

class Division(Organization): 
    parent_org = models.ForeignKey(Organization) 

    class Meta: 
     unique_together=['parent_org', 'alias'] 
     ... 

試圖執行syncdb給我這個錯誤:

Error: One or more models did not validate: 
organizations.division: "unique_together" refers to alias. This is not in the 
same model as the unique_together statement. 

任何幫助表示讚賞,

謝謝,

埃裏克

+0

你能解釋的規定較多,無法理解什麼是繼承組織有外鍵相同的基本模型的需要。 – 2010-10-05 19:25:35

+0

這是一個簡單的親子關係一個組織可以有多個部門,一個部門是某種專門的組織。 – 2010-10-05 19:32:30

回答

12

這是設計。讀爲unique_together選項的文檔,它指出:

It's used in the Django admin and is enforced at the database level.

如果你看看那一個子類創建表,你會發現它並不實際上有其父擁有的字段。相反,它會爲父表獲得一個名爲[field]_ptr_id的字段名稱的軟外鍵,其中[field]是您從中排除應用程序名稱而繼承的表的名稱。所以你的分區表有一個名爲organization_ptr_id的主要外鍵。

現在因爲unique_together在數據庫級使用UNIQUE約束強制執行,所以我不知道數據庫實際上是否將其應用於不在表中的字段。

最好的辦法可能是在業務邏輯層面使用Validators,或者重新考慮數據庫模式以支持約束。

編輯:正如Manoj指出的那樣,您也可以嘗試使用Model Validators,如validate_unique

+1

+1提到爲什麼並鏈接到驗證器。您可能還想指出_model驗證。例如'validate_unique':http:// docs。djangoproject.com/en/dev/ref/models/instances/?from=olddocs#django.db.models.Model.validate_unique – 2010-10-06 05:12:49

4

[模型]驗證器將爲你工作。也許最簡單的,雖然是使用:

class BaseOrganization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    class Meta: 
     abstract = True 

class Organization(BaseOrganization): 
    pass 

class Division(BaseOrganization): 
    parent_org = models.ForeignKey(Organization) 

    class Meta: 
     unique_together=['parent_org', 'alias'] 

注:與當前的代碼,你不能有分歧的細分。

1

這是我最近在Django 1.6中使用的解決方案(感謝馬諾Govindan的想法):

class Organization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    ... 

class Division(Organization): 
    parent_org = models.ForeignKey(Organization) 

    # override Model.validate_unique 
    def validate_unique(self, exclude=None):  
     # these next 5 lines are directly from the Model.validate_unique source code 
     unique_checks, date_checks = self._get_unique_checks(exclude=exclude) 
     errors = self._perform_unique_checks(unique_checks) 
     date_errors = self._perform_date_checks(date_checks) 
     for k, v in date_errors.items(): 
      errors.setdefault(k, []).extend(v) 

     # here I get a list of all pairs of parent_org, alias from the database (returned 
     # as a list of tuples) & check for a match, in which case you add a non-field 
     # error to the error list 
     pairs = Division.objects.exclude(pk=self.pk).values_list('parent_org', 'alias') 
     if (self.parent_org, self.alias) in pairs: 
       errors.setdefault(NON_FIELD_ERRORS, []).append('parent_org and alias must be unique') 

     # finally you raise the ValidationError that includes all validation errors, 
     # including your new unique constraint 
     if errors: 
      raise ValidationError(errors) 
+0

很明顯,這不涉及任何數據庫級別的檢查,因爲表內的唯一約束可能會在Django驗證方面,這個解決方案對我來說是無縫的。我必須解決的一個問題是確保排除調用validate_unique的實例(因此排除(pk = self.pk))。 – 2014-02-04 17:41:12

2

這並不嚴格適用的問題,但很密切的關係;如果基類是抽象的,則可以使用unique_together。您可以使用標記的抽象模型階級作爲階級:

class Meta(): 
    abstract = True 

這將防止從Django的爲類創建表,其字段將被直接計入任何子類。在這種情況下,unique_together是可能的,因爲所有字段都在同一個表中。

https://docs.djangoproject.com/en/1.5/topics/db/models/#abstract-base-classes