summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJon Dufresne <jon.dufresne@gmail.com>2020-11-05 01:40:41 -0800
committerGitHub <noreply@github.com>2020-11-05 10:40:41 +0100
commit859cd7c6b43bf70e2852eda10f635c70feeb397f (patch)
tree648e43ff9d72edcf162583a123fbdfe047fd0f02 /django
parent76181308fb02e67794d0cc1471766a5d7e4c877e (diff)
Fixed #22276 -- Fixed crash when formset management form is invalid.
Co-authored-by: Patryk Zawadzki <patrys@room-303.com>
Diffstat (limited to 'django')
-rw-r--r--django/forms/formsets.py53
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():