Django ManyToMany model validation

2019-01-13 20:41发布

I have a model with a ManyToManyField similar to this one (the model Word has a language, too):

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')

When trying to add a new sentence (e.g. through django admin) I get 'Sentence' instance needs to have a primary key value before a many-to-many relationship can be used. This means I can't access self.words before saving it, but this is exactly what I'm trying to do. Is there any way to work around this so you can validate this model nevertheless? I really want to directly validate the model's fields.

I found many questions concerning this exception, but I couldn't find help for my problem. I would appreciate any suggestions!

2条回答
一夜七次
2楼-- · 2019-01-13 20:51

You can't do it from the clean method on the model. It's simply not possible with the way M2M relationships work in Django. However, you can do this sort of validation on forms used to create a Sentence such as in the admin or a form on your site.

查看更多
Emotional °昔
3楼-- · 2019-01-13 21:02

It is not possible to do this validation in the model's clean method, but you can create a model form which can validate the choice of 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

You can then customise your Sentence model admin class, to use your form in the Django admin.

class SentenceAdmin(admin.ModelAdmin):
    form = SentenceForm

admin.register(Sentence, SentenceAdmin)
查看更多
登录 后发表回答