2011-11-02 111 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的表单上进行这种验证,例如在管理员或您网站的表单中。