Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ManyToMany model validation

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!

like image 558
purefanatic Avatar asked Nov 02 '11 19:11

purefanatic


2 Answers

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         fields = ['words', 'language']      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) 
like image 147
Alasdair Avatar answered Sep 19 '22 16:09

Alasdair


According to Django docs you can listen to the m2m_changed signal, which will trigger pre_add and post_add actions.

Using add() with a many-to-many relationship, however, will not call any save() methods (the bulk argument doesn’t exist), but rather create the relationships using QuerySet.bulk_create(). If you need to execute some custom logic when a relationship is created, listen to the m2m_changed signal, which will trigger pre_add and post_add actions.

like image 41
M.Void Avatar answered Sep 18 '22 16:09

M.Void