Fixed #29205 -- Corrected rendering of required attributes for MultiValueField subfields.

This commit is contained in:
Jacob Walls 2021-02-22 14:28:38 -05:00 committed by Carlton Gibson
parent db1fc5cd3c
commit 2d0ae8da80
2 changed files with 34 additions and 2 deletions

View File

@ -2,7 +2,7 @@ import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.forms.utils import flatatt, pretty_name from django.forms.utils import flatatt, pretty_name
from django.forms.widgets import Textarea, TextInput from django.forms.widgets import MultiWidget, Textarea, TextInput
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import conditional_escape, format_html, html_safe from django.utils.html import conditional_escape, format_html, html_safe
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -233,7 +233,17 @@ class BoundField:
widget = widget or self.field.widget widget = widget or self.field.widget
attrs = dict(attrs) # Copy attrs to avoid modifying the argument. attrs = dict(attrs) # Copy attrs to avoid modifying the argument.
if widget.use_required_attribute(self.initial) and self.field.required and self.form.use_required_attribute: if widget.use_required_attribute(self.initial) and self.field.required and self.form.use_required_attribute:
attrs['required'] = True # MultiValueField has require_all_fields: if False, fall back
# on subfields.
if (
hasattr(self.field, 'require_all_fields') and
not self.field.require_all_fields and
isinstance(self.field.widget, MultiWidget)
):
for subfield, subwidget in zip(self.field.fields, widget.widgets):
subwidget.attrs['required'] = subwidget.use_required_attribute(self.initial) and subfield.required
else:
attrs['required'] = True
if self.field.disabled: if self.field.disabled:
attrs['disabled'] = True attrs['disabled'] = True
return attrs return attrs

View File

@ -10,6 +10,20 @@ from django.test import SimpleTestCase
beatles = (('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')) beatles = (('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))
class PartiallyRequiredField(MultiValueField):
def compress(self, data_list):
return ','.join(data_list) if data_list else None
class PartiallyRequiredForm(Form):
f = PartiallyRequiredField(
fields=(CharField(required=True), CharField(required=False)),
required=True,
require_all_fields=False,
widget=MultiWidget(widgets=[TextInput(), TextInput()]),
)
class ComplexMultiWidget(MultiWidget): class ComplexMultiWidget(MultiWidget):
def __init__(self, attrs=None): def __init__(self, attrs=None):
widgets = ( widgets = (
@ -172,3 +186,11 @@ class MultiValueFieldTest(SimpleTestCase):
}) })
form.is_valid() form.is_valid()
self.assertEqual(form.cleaned_data['field1'], 'some text,JP,2007-04-25 06:24:00') self.assertEqual(form.cleaned_data['field1'], 'some text,JP,2007-04-25 06:24:00')
def test_render_required_attributes(self):
form = PartiallyRequiredForm({'f_0': 'Hello', 'f_1': ''})
self.assertTrue(form.is_valid())
self.assertInHTML('<input type="text" name="f_0" value="Hello" required id="id_f_0">', form.as_p())
self.assertInHTML('<input type="text" name="f_1" id="id_f_1">', form.as_p())
form = PartiallyRequiredForm({'f_0': '', 'f_1': ''})
self.assertFalse(form.is_valid())