2011-11-02 152 views
30

我有一個ManyToManyField類似這樣的一個模型(模型字有語言,太):Django的多對多模型驗證

class Sentence(models.Model): 
    words = models.ManyToManyField(Word) 
    language = models.ForeignKey(Language) 
    def clean(self): 
     for word in self.words.all(): 
      if word.language_id != self.language_id: 
       raise ValidationError('One of the words has a false language') 

當試圖添加一個新的句子(例如,通過Django管理)我得到'Sentence' instance needs to have a primary key value before a many-to-many relationship can be used。這意味着在保存之前我無法訪問self.words,但這正是我想要做的。有沒有辦法解決這個問題,所以你可以驗證這個模型呢?我真的想直接驗證模型的字段。

我發現了很多關於這個異常的問題,但是我找不到對我的問題的幫助。我將不勝感激任何建議!

+0

如果你想創建一個Word,你會如何驗證它是否與一個句子相關聯?它的模型定義中沒有「句子」字段。 – johnklawlor

+0

並非所有人都使用表單。我認爲這在Django中是一個巨大的缺陷。有沒有人有更好的答案? –

回答

44

不可能在模型的clean方法中進行此驗證,但您可以創建一個模型表單來驗證words的選擇。

from django import forms 

class SentenceForm(forms.ModelForm): 
    class Meta: 
     model = Sentence 

    def clean(self): 
     """ 
     Checks that all the words belong to the sentence's language. 
     """ 
     words = self.cleaned_data.get('words') 
     language = self.cleaned_data.get('language') 
     if language and words: 
      # only check the words if the language is valid 
      for word in words: 
       if words.language != language: 
        raise ValidationError("The word %s has a different language" % word) 
     return self.cleaned_data 

然後,您可以自定義Sentence模型管理類,使用您的形式Django管理。

class SentenceAdmin(admin.ModelAdmin): 
    form = SentenceForm 

admin.register(Sentence, SentenceAdmin) 
+6

很遺憾,看不到在模型中直接驗證它的可能性。但到目前爲止,自定義的ModelForm對我來說已經足夠了。謝謝您的回答! – purefanatic

+0

另外,如果您的Sentence格式也可以被視爲另一個模型(例如段落)的內聯格式,那麼您還需要將'form = SentenceForm'這一行添加到SentenceInline類中。 –

+0

@purefanatic'Model.save()'不會引發'ValidationErrors',因此無法直接進行驗證。 – jnns

1

你不能從模型上的clean方法做到這一點。 M2M關係在Django中的工作方式根本無法實現。但是,您可以在用於創建Sentence的表單上進行這種驗證,例如在管理員或您網站的表單中。