[4.0.x] Fixed #33346 -- Fixed SimpleTestCase.assertFormsetError() crash on a formset named "form".

Thanks OutOfFocus4 for the report.

Regression in 456466d932.

Backport of cb383753c0 from main.
This commit is contained in:
Baptiste Mispelon 2021-12-08 14:46:22 +01:00 committed by Mariusz Felisiak
parent b7f2afa8de
commit 15031852c5
3 changed files with 46 additions and 3 deletions

View File

@ -557,7 +557,7 @@ class SimpleTestCase(unittest.TestCase):
# Search all contexts for the error. # Search all contexts for the error.
found_formset = False found_formset = False
for i, context in enumerate(contexts): for i, context in enumerate(contexts):
if formset not in context: if formset not in context or not hasattr(context[formset], 'forms'):
continue continue
found_formset = True found_formset = True
for err in errors: for err in errors:

View File

@ -9,4 +9,6 @@ Django 4.0.1 fixes several bugs in 4.0.
Bugfixes Bugfixes
======== ========
* ... * Fixed a regression in Django 4.0 that caused a crash of
:meth:`~django.test.SimpleTestCase.assertFormsetError` on a formset named
``form`` (:ticket:`33346`).

View File

@ -13,7 +13,10 @@ from django.core.files.storage import default_storage
from django.db import ( from django.db import (
IntegrityError, connection, connections, models, router, transaction, IntegrityError, connection, connections, models, router, transaction,
) )
from django.forms import EmailField, IntegerField from django.forms import (
CharField, EmailField, Form, IntegerField, ValidationError,
formset_factory,
)
from django.http import HttpResponse from django.http import HttpResponse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.test import ( from django.test import (
@ -1241,6 +1244,44 @@ class AssertURLEqualTests(SimpleTestCase):
) )
class TestForm(Form):
field = CharField()
def clean_field(self):
value = self.cleaned_data.get('field', '')
if value == 'invalid':
raise ValidationError('invalid value')
return value
class TestFormset(formset_factory(TestForm)):
@classmethod
def _get_cleaned_formset(cls, field_value):
formset = cls({
'form-TOTAL_FORMS': '1',
'form-INITIAL_FORMS': '0',
'form-0-field': field_value,
})
formset.full_clean()
return formset
@classmethod
def invalid(cls):
return cls._get_cleaned_formset('invalid')
class AssertFormsetErrorTests(SimpleTestCase):
def test_formset_named_form(self):
formset = TestFormset.invalid()
# The mocked context emulates the template-based rendering of the
# formset.
response = mock.Mock(context=[
{'form': formset},
{'form': formset.management_form},
])
self.assertFormsetError(response, 'form', 0, 'field', 'invalid value')
class FirstUrls: class FirstUrls:
urlpatterns = [path('first/', empty_response, name='first')] urlpatterns = [path('first/', empty_response, name='first')]