diff --git a/betterforms/multiform.py b/betterforms/multiform.py index ab8d5f0..8203345 100644 --- a/betterforms/multiform.py +++ b/betterforms/multiform.py @@ -71,6 +71,8 @@ def is_valid(self): def non_field_errors(self): return ErrorList(chain.from_iterable( form.non_field_errors() for form in self.forms.values() + # FormSets don't have non_field_errors errors + if hasattr(form, 'non_field_errors') )) def as_table(self): diff --git a/docs/multiform.rst b/docs/multiform.rst index 6cd3a71..eb93311 100644 --- a/docs/multiform.rst +++ b/docs/multiform.rst @@ -289,6 +289,21 @@ API Reference .. method:: non_field_errors + .. note:: + + :class:`FormSets ` do not + provide :meth:`non_field_errors`, they provide + :meth:`non_form_errors() `, + if you put a :class:`FormSet + ` in your + :attr:`form_classes`, the output of :meth:`non_field_errors` **does + not** include the + :meth:`non_form_errors() `, + from the formset, you will + need to call + :meth:`non_form_errors() `, + yourself. + .. method:: as_table .. method:: as_ul diff --git a/tests/tests/forms.py b/tests/tests/forms.py index ad669d8..9537eba 100644 --- a/tests/tests/forms.py +++ b/tests/tests/forms.py @@ -4,12 +4,13 @@ from django.utils.datastructures import SortedDict as OrderedDict # NOQA from django import forms +from django.forms.models import inlineformset_factory from django.contrib.admin import widgets as admin_widgets from django.core.exceptions import ValidationError from betterforms.multiform import MultiForm, MultiModelForm -from .models import User, Profile, Badge, Author +from .models import User, Profile, Badge, Author, Book, BookImage class UserForm(forms.ModelForm): @@ -100,3 +101,29 @@ class ManyToManyMultiForm(MultiModelForm): 'badge': BadgeForm, 'author': AuthorForm, } + + +class BookForm(forms.ModelForm): + class Meta: + model = Book + fields = ('name',) + + +BookImageFormSet = inlineformset_factory(Book, BookImage, fields=('name',)) + + +class BookMultiForm(MultiModelForm): + form_classes = { + 'book': BookForm, + 'error': RaisesErrorForm, + 'images': BookImageFormSet, + } + + def __init__(self, *args, **kwargs): + instance = kwargs.pop('instance', None) + if instance is not None: + kwargs['instance'] = { + 'book': instance, + 'images': instance, + } + super(BookMultiForm, self).__init__(*args, **kwargs) diff --git a/tests/tests/models.py b/tests/tests/models.py index 563118f..67e8ea1 100644 --- a/tests/tests/models.py +++ b/tests/tests/models.py @@ -24,3 +24,8 @@ class Author(models.Model): class Book(models.Model): name = models.CharField(max_length=255) + + +class BookImage(models.Model): + book = models.ForeignKey(Book, related_name='images') + name = models.CharField(max_length=255) diff --git a/tests/tests/tests.py b/tests/tests/tests.py index 4d4f2f3..2de1a7f 100644 --- a/tests/tests/tests.py +++ b/tests/tests/tests.py @@ -15,7 +15,7 @@ from .models import User, Profile, Badge, Book from .forms import ( UserProfileMultiForm, BadgeMultiForm, ErrorMultiForm, - MixedForm, NeedsFileField, ManyToManyMultiForm, + MixedForm, NeedsFileField, ManyToManyMultiForm, BookMultiForm, ) @@ -244,3 +244,14 @@ def test_works_with_create_view_post(self): resp = viewfn(request) self.assertEqual(resp.status_code, 302) self.assertEqual(Badge.objects.count(), 2) + + def test_non_field_errors_with_formset(self): + form = BookMultiForm({ + 'book-name': '', + 'images-0-name': '', + 'images-TOTAL_FORMS': '3', + 'images-INITIAL_FORMS': '0', + 'images-MAX_NUM_FORMS': '1000', + }) + # assertDoesntRaise AttributeError + self.assertEqual(form.non_field_errors().as_text(), '* It broke')