diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index c8e5893f19..c8596b0465 100644
a
|
b
|
class BaseFormSet(RenderableFormMixin):
|
280 | 280 | ) |
281 | 281 | return [form.cleaned_data for form in self.forms] |
282 | 282 | |
| 283 | @cached_property |
| 284 | def forms_valid(self): |
| 285 | return all( |
| 286 | [ |
| 287 | form.is_valid() |
| 288 | for form in self.forms |
| 289 | if not (self.can_delete and self._should_delete_form(form)) |
| 290 | ] |
| 291 | ) |
| 292 | |
283 | 293 | @property |
284 | 294 | def deleted_forms(self): |
285 | 295 | """Return a list of forms that have been marked for deletion.""" |
286 | | if not self.is_valid() or not self.can_delete: |
| 296 | if not self.forms_valid or not self.can_delete: |
287 | 297 | return [] |
288 | 298 | # construct _deleted_form_indexes which is just a list of form indexes |
289 | 299 | # that have had their deletion widget set to True |
… |
… |
class BaseFormSet(RenderableFormMixin):
|
303 | 313 | Return a list of form in the order specified by the incoming data. |
304 | 314 | Raise an AttributeError if ordering is not allowed. |
305 | 315 | """ |
306 | | if not self.is_valid() or not self.can_order: |
| 316 | if not self.forms_valid or not self.can_order: |
307 | 317 | raise AttributeError( |
308 | 318 | "'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__ |
309 | 319 | ) |
… |
… |
class BaseFormSet(RenderableFormMixin):
|
384 | 394 | self.errors |
385 | 395 | # List comprehension ensures is_valid() is called for all forms. |
386 | 396 | # Forms due to be deleted shouldn't cause the formset to be invalid. |
387 | | forms_valid = all( |
388 | | [ |
389 | | form.is_valid() |
390 | | for form in self.forms |
391 | | if not (self.can_delete and self._should_delete_form(form)) |
392 | | ] |
393 | | ) |
394 | | return forms_valid and not self.non_form_errors() |
| 397 | return self.forms_valid and not self.non_form_errors() |
395 | 398 | |
396 | 399 | def full_clean(self): |
397 | 400 | """ |