diff options
Diffstat (limited to 'django/forms')
| -rw-r--r-- | django/forms/formsets.py | 53 |
1 files changed, 39 insertions, 14 deletions
diff --git a/django/forms/formsets.py b/django/forms/formsets.py index c921da72f5..a89c35599f 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -6,7 +6,7 @@ from django.forms.widgets import HiddenInput, NumberInput from django.utils.functional import cached_property from django.utils.html import html_safe from django.utils.safestring import mark_safe -from django.utils.translation import gettext as _, ngettext +from django.utils.translation import gettext_lazy as _, ngettext __all__ = ('BaseFormSet', 'formset_factory', 'all_valid') @@ -41,6 +41,14 @@ class ManagementForm(Form): self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) super().__init__(*args, **kwargs) + def clean(self): + cleaned_data = super().clean() + # When the management form is invalid, we don't know how many forms + # were submitted. + cleaned_data.setdefault(TOTAL_FORM_COUNT, 0) + cleaned_data.setdefault(INITIAL_FORM_COUNT, 0) + return cleaned_data + @html_safe class BaseFormSet: @@ -48,9 +56,16 @@ class BaseFormSet: A collection of instances of the same Form class. """ ordering_widget = NumberInput + default_error_messages = { + 'missing_management_form': _( + 'ManagementForm data is missing or has been tampered with. Missing fields: ' + '%(field_names)s. You may need to file a bug report if the issue persists.' + ), + } def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, - initial=None, error_class=ErrorList, form_kwargs=None): + initial=None, error_class=ErrorList, form_kwargs=None, + error_messages=None): self.is_bound = data is not None or files is not None self.prefix = prefix or self.get_default_prefix() self.auto_id = auto_id @@ -62,6 +77,13 @@ class BaseFormSet: self._errors = None self._non_form_errors = None + messages = {} + for cls in reversed(type(self).__mro__): + messages.update(getattr(cls, 'default_error_messages', {})) + if error_messages is not None: + messages.update(error_messages) + self.error_messages = messages + def __str__(self): return self.as_table() @@ -88,18 +110,7 @@ class BaseFormSet: """Return the ManagementForm instance for this FormSet.""" if self.is_bound: form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix) - if not form.is_valid(): - raise ValidationError( - _( - 'ManagementForm data is missing or has been tampered ' - 'with. Missing fields: %(field_names)s' - ) % { - 'field_names': ', '.join( - form.add_prefix(field_name) for field_name in form.errors - ), - }, - code='missing_management_form', - ) + form.full_clean() else: form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={ TOTAL_FORM_COUNT: self.total_form_count(), @@ -327,6 +338,20 @@ class BaseFormSet: if not self.is_bound: # Stop further processing. return + + if not self.management_form.is_valid(): + error = ValidationError( + self.error_messages['missing_management_form'], + params={ + 'field_names': ', '.join( + self.management_form.add_prefix(field_name) + for field_name in self.management_form.errors + ), + }, + code='missing_management_form', + ) + self._non_form_errors.append(error) + for i, form in enumerate(self.forms): # Empty forms are unchanged forms beyond those with initial data. if not form.has_changed() and i >= self.initial_form_count(): |
