summaryrefslogtreecommitdiff
path: root/tests/forms_tests
diff options
context:
space:
mode:
authorMatthias Kestenholz <mk@feinheit.ch>2025-01-15 21:04:26 +0100
committerGitHub <noreply@github.com>2025-01-15 17:04:26 -0300
commit6a7ee02f5994c65bbefe92a3da74f22326970cf9 (patch)
treed49780a99dbb0ad8b85522b55dcaf54fafde0264 /tests/forms_tests
parent0cabed9efa2c7abd1693860069f20ec5db41fcd8 (diff)
Fixed #35521 -- Allowed overriding BoundField class on fields, forms and renderers.
Thank you Sarah Boyce, Carlton Gibson, Tim Schilling and Adam Johnson for reviews. Co-authored-by: Christophe Henry <contact@c-henry.fr> Co-authored-by: David Smith <smithdc@gmail.com> Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> Co-authored-by: Matthias Kestenholz <mk@feinheit.ch>
Diffstat (limited to 'tests/forms_tests')
-rw-r--r--tests/forms_tests/tests/test_forms.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index b41424d43d..20c86754c6 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -8,6 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import MaxValueValidator, RegexValidator
from django.forms import (
BooleanField,
+ BoundField,
CharField,
CheckboxSelectMultiple,
ChoiceField,
@@ -4971,6 +4972,22 @@ class RendererTests(SimpleTestCase):
context = form.get_context()
self.assertEqual(context["errors"].renderer, custom)
+ def test_boundfield_fallback(self):
+ class RendererWithoutBoundFieldClassAttribute:
+ form_template_name = "django/forms/div.html"
+ formset_template_name = "django/forms/formsets/div.html"
+ field_template_name = "django/forms/field.html"
+
+ def render(self, template_name, context, request=None):
+ return "Nice"
+
+ class UserForm(Form):
+ name = CharField()
+
+ form = UserForm(renderer=RendererWithoutBoundFieldClassAttribute())
+ self.assertIsInstance(form["name"], BoundField)
+ self.assertEqual(form["name"].as_field_group(), "Nice")
+
class TemplateTests(SimpleTestCase):
def test_iterate_radios(self):
@@ -5473,3 +5490,146 @@ class OverrideTests(SimpleTestCase):
'<label for="id_name" class="required">Name:</label>'
'<legend class="required">Language:</legend>',
)
+
+
+class BoundFieldWithoutColon(BoundField):
+ def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
+ return super().label_tag(
+ contents=contents, attrs=attrs, label_suffix="", tag=None
+ )
+
+
+class BoundFieldWithTwoColons(BoundField):
+ def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
+ return super().label_tag(
+ contents=contents, attrs=attrs, label_suffix="::", tag=None
+ )
+
+
+class BoundFieldWithCustomClass(BoundField):
+ def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
+ attrs = attrs or {}
+ attrs["class"] = "custom-class"
+ return super().label_tag(contents, attrs, label_suffix, tag)
+
+
+class BoundFieldWithWrappingClass(BoundField):
+ def css_classes(self, extra_classes=None):
+ parent_classes = super().css_classes(extra_classes)
+ return f"field-class {parent_classes}"
+
+
+class BoundFieldOverrideRenderer(DjangoTemplates):
+ bound_field_class = BoundFieldWithoutColon
+
+
+@override_settings(
+ FORM_RENDERER="forms_tests.tests.test_forms.BoundFieldOverrideRenderer"
+)
+class CustomBoundFieldTest(SimpleTestCase):
+ def test_renderer_custom_bound_field(self):
+ t = Template("{{ form }}")
+ html = t.render(Context({"form": Person()}))
+ expected = """
+ <div><label for="id_first_name">First name</label>
+ <input type="text" name="first_name" required
+ id="id_first_name"></div>
+ <div><label for="id_last_name">Last name</label>
+ <input type="text" name="last_name" required
+ id="id_last_name"></div><div>
+ <label for="id_birthday">Birthday</label>
+ <input type="text" name="birthday" required
+ id="id_birthday"></div>"""
+ self.assertHTMLEqual(html, expected)
+
+ def test_form_custom_boundfield(self):
+ class CustomBoundFieldPerson(Person):
+ bound_field_class = BoundFieldWithTwoColons
+
+ with self.subTest("form's BoundField takes over renderer's BoundField"):
+ t = Template("{{ form }}")
+ html = t.render(Context({"form": CustomBoundFieldPerson()}))
+ expected = """
+ <div><label for="id_first_name">First name::</label>
+ <input type="text" name="first_name" required
+ id="id_first_name"></div>
+ <div><label for="id_last_name">Last name::</label>
+ <input type="text" name="last_name" required
+ id="id_last_name"></div><div>
+ <label for="id_birthday">Birthday::</label>
+ <input type="text" name="birthday" required
+ id="id_birthday"></div>"""
+ self.assertHTMLEqual(html, expected)
+
+ with self.subTest("Constructor argument takes over class property"):
+ t = Template("{{ form }}")
+ html = t.render(
+ Context(
+ {
+ "form": CustomBoundFieldPerson(
+ bound_field_class=BoundFieldWithCustomClass
+ )
+ }
+ )
+ )
+ expected = """
+ <div><label class="custom-class" for="id_first_name">First name:</label>
+ <input type="text" name="first_name" required
+ id="id_first_name"></div>
+ <div><label class="custom-class" for="id_last_name">Last name:</label>
+ <input type="text" name="last_name" required
+ id="id_last_name"></div><div>
+ <label class="custom-class" for="id_birthday">Birthday:</label>
+ <input type="text" name="birthday" required
+ id="id_birthday"></div>"""
+ self.assertHTMLEqual(html, expected)
+
+ with self.subTest("Overriding css_classes works as expected"):
+ t = Template("{{ form }}")
+ html = t.render(
+ Context(
+ {
+ "form": CustomBoundFieldPerson(
+ bound_field_class=BoundFieldWithWrappingClass
+ )
+ }
+ )
+ )
+ expected = """
+ <div class="field-class"><label for="id_first_name">First name:</label>
+ <input type="text" name="first_name" required
+ id="id_first_name"></div>
+ <div class="field-class"><label for="id_last_name">Last name:</label>
+ <input type="text" name="last_name" required
+ id="id_last_name"></div><div class="field-class">
+ <label for="id_birthday">Birthday:</label>
+ <input type="text" name="birthday" required
+ id="id_birthday"></div>"""
+ self.assertHTMLEqual(html, expected)
+
+ def test_field_custom_bound_field(self):
+ class BoundFieldWithTwoColonsCharField(CharField):
+ bound_field_class = BoundFieldWithTwoColons
+
+ class CustomFieldBoundFieldPerson(Person):
+ bound_field_class = BoundField
+
+ first_name = BoundFieldWithTwoColonsCharField()
+ last_name = BoundFieldWithTwoColonsCharField(
+ bound_field_class=BoundFieldWithCustomClass
+ )
+
+ html = Template("{{ form }}").render(
+ Context({"form": CustomFieldBoundFieldPerson()})
+ )
+ expected = """
+ <div><label for="id_first_name">First name::</label>
+ <input type="text" name="first_name" required
+ id="id_first_name"></div>
+ <div><label class="custom-class" for="id_last_name">Last name:</label>
+ <input type="text" name="last_name" required
+ id="id_last_name"></div><div>
+ <label for="id_birthday">Birthday:</label>
+ <input type="text" name="birthday" required
+ id="id_birthday"></div>"""
+ self.assertHTMLEqual(html, expected)