Reputation: 41
I have searched all over and all I can find is stuff from years ago and/or stuff that doesn't apply.
I'm trying to add a MultiValueField to my form so people can easily type in one to three inputs. Only the first of these fields should be required, but the form validator is still requiring all three unless I make the whole thing optional (which it shouldn't be).
Below is the code for my MultiWidget, MultiValueField, and form. I've tried removing everything from the field attributes except for required=False
and it still required all of them. I tried setting require_all_fields=False
in the form when I call the field instead of in the field __init__
and it still required all of them.
I feel like I've read and read and read and there's very little information on how to implement this sort of thing.
class ThreeNumFields(forms.MultiWidget):
def __init__(self, attrs=None):
self.widgets = [
forms.TextInput(),
forms.TextInput(),
forms.TextInput()
]
super().__init__(self.widgets, attrs)
def decompress(self, value):
if value:
return value.split(' ')
return [None, None]
class LocationMultiField(forms.MultiValueField):
widget = ThreeNumFields()
validators = [RegexValidator]
def __init__(self):
fields = (
forms.CharField(
error_messages={'incomplete': 'Please enter at least one valid zip code.'},
validators=[
RegexValidator(r'^[0-9]{5}$', 'Enter a valid US zip code.'),
],
max_length=5,
),
forms.CharField(
required=False,
validators=[
RegexValidator(r'^[0-9]{5}$', 'Enter a valid US zip code.'),
],
max_length=5,
),
forms.CharField(
required=False,
validators=[
RegexValidator(r'^[0-9]{5}$', 'Enter a valid US zip code.'),
],
max_length=5,
)
)
super(LocationMultiField, self).__init__(
fields=fields,
require_all_fields=False,
)
def compress(self, data_list):
return ' '.join(data_list)
class NewForecastForm(forms.ModelForm):
class Meta:
model = ForecastProfile
exclude = ['userid', 'last_updated']
nickname = forms.CharField(
label=_('Forecast Nickname')
)
locations = LocationMultiField()
timezone = forms.ChoiceField(
label=_('Timezone for your forecast'),
choices=choices.timezones
)
start_time = forms.ChoiceField(
label=_('Earliest time you want in your forecast'),
help_text=_('(Time will not be exact to account for timezone conversions and forecast data.)'),
choices=choices.times
)
end_time = forms.ChoiceField(
label=_('Latest time you want in your forecast'),
help_text=_('(Time will not be exact to account for timezone conversions and forecast data.)'),
choices=choices.times
)
alerts = forms.MultipleChoiceField(
choices=choices.alerts,
widget=forms.CheckboxSelectMultiple()
)
days_in_forecast = forms.ChoiceField(
choices=choices.forecastdays
)
def clean(self):
cleaned_data = super(NewForecastForm, self).clean()
start = cleaned_data.get("start_time")
end = cleaned_data.get("end_time")
if start > end:
raise forms.ValidationError(
"Your start time must be before your end time."
)
Upvotes: 3
Views: 2111
Reputation: 1446
I ran into the same problem and it looks like a bug with Django. There's an open ticket, #29205.
That said, I did get the form to work correctly in my case by setting required=False
, require_all_fields=False
, and explicitly adding required to the widget attrs in the init method of the form.
In your case that would mean putting self.fields['locations'].widget.widgets[0].attrs['required'] = True
in the init method of NewForecastForm
Upvotes: 1