diff --git a/django/forms/models.py b/django/forms/models.py
index e6e1203..b248eca 100644
a
|
b
|
class BaseModelForm(BaseForm):
|
431 | 431 | except ValidationError as e: |
432 | 432 | self._update_errors(e) |
433 | 433 | |
| 434 | # Validation of m2m data. Refs. #19671 |
| 435 | for f in self.instance._meta.many_to_many + self.instance._meta.virtual_fields: |
| 436 | if exclude and f.name in exclude: |
| 437 | continue |
| 438 | if f.name in self.cleaned_data: |
| 439 | try: |
| 440 | f.clean(self.cleaned_data[f.name], self.instance) |
| 441 | except ValidationError as e: |
| 442 | e.error_dict = {f.name: e} |
| 443 | self._update_errors(e) |
| 444 | |
434 | 445 | # Validate uniqueness if needed. |
435 | 446 | if self._validate_unique: |
436 | 447 | self.validate_unique() |
diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py
index d327a5f..a0aa5fd 100644
a
|
b
|
class StumpJoke(models.Model):
|
407 | 407 | class Student(models.Model): |
408 | 408 | character = models.ForeignKey(Character) |
409 | 409 | study = models.CharField(max_length=30) |
| 410 | |
| 411 | |
| 412 | def validate_m2m_field(value): |
| 413 | raise ValidationError('Many to many validator called') |
| 414 | |
| 415 | |
| 416 | # Model for #19671 |
| 417 | class M2MCustomValidate(models.Model): |
| 418 | authors = models.ManyToManyField(Author, validators=[validate_m2m_field]) |
diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py
index 783a031..fdcbc1a 100644
a
|
b
|
from .models import (Article, ArticleStatus, Author, Author1, BetterWriter, BigI
|
22 | 22 | DerivedBook, DerivedPost, Document, ExplicitPK, FilePathModel, FlexibleDatePost, Homepage, |
23 | 23 | ImprovedArticle, ImprovedArticleWithParentLink, Inventory, Person, Post, Price, |
24 | 24 | Product, Publication, TextFile, Triple, Writer, WriterProfile, |
25 | | Colour, ColourfulItem, DateTimePost, CustomErrorMessage, |
| 25 | Colour, ColourfulItem, DateTimePost, CustomErrorMessage, M2MCustomValidate, |
26 | 26 | test_images, StumpJoke, Character, Student) |
27 | 27 | |
28 | 28 | if test_images: |
… |
… |
class ModelFormBaseTest(TestCase):
|
227 | 227 | obj = f2.save() |
228 | 228 | self.assertEqual(obj.character, char) |
229 | 229 | |
| 230 | def test_m2m_field_custom_validate(self): |
| 231 | class M2MCustomValidateForm(forms.ModelForm): |
| 232 | class Meta: |
| 233 | model = M2MCustomValidate |
| 234 | fields = '__all__' |
| 235 | |
| 236 | author = Author.objects.create(full_name='Anubhav Joshi') |
| 237 | form = M2MCustomValidateForm({'authors': [author.pk]}) |
| 238 | self.assertEqual(list(form.errors), ['authors']) |
| 239 | |
230 | 240 | def test_missing_fields_attribute(self): |
231 | 241 | message = ( |
232 | 242 | "Creating a ModelForm without either the 'fields' attribute " |