I am using formset_factory to manage a couple identical forms on my page. In each form, there is a pair of chained dropdowns. DropdownA has an onchange event that request options for dropdownB (AJAX). This all works fine but when I go to submit my forms via a POST request, they all fail the forms.is_valid()
check. Printing the errors of the submitted formset reveals why:
[{'DropdownB ': ['Select a valid choice. Like is not one of the available choices.']}, {'DropdownB ': ['Select a valid choice. < is not one of the available choices.']}]
There are two errors, one for each form. They are both complaining that the choice sent for DropdownB is not one of the available ('Like' and '<' respectfully).
Now, because I want to only populate DropdownB with certain choices based on what DropdownA selected, I purposefully defined DropdownB (a choicefield) with 0 choices.
DropdownB = ()
DropdownB = forms.ChoiceField(choices=op_choices, required=False)
How do I specify to the server what the valid choices are BASED ON what DropdownA's value is?
I tried to simplify this problem in the abstract above, but if you want the full code of the form, here you go:
class UnifiedSingleSearchBar(forms.Form):
# Dict to categorize field types
type_dict = {
'DateField': 'Numeric',
'DateTimeField': 'Numeric',
'AutoField': 'Numeric',
'CharField': 'String',
'BooleanField': 'Bool',
}
operation_dict = {'Numeric':
(
('>', '>'),
('>=', '>='),
('<', '<'),
('<=', '<='),
('=', '='),
('>-<', 'Between'),
('0', 'IS null'),
('1', 'IS NOT null'),
),
'String':
(
('Like', 'Like'),
('Is', 'Is')
),
'Bool':
(
('True', 'True'),
('False', 'False')
)
}
searchabel_field_choices = ()
# To create the "field" dropdown, we loop through every field in the model and note its type.
for field in Mymodel._meta.fields:
tuple = (
(field.name, field.name), # signifies a nested tuple
)
searchabel_field_choices = searchabel_field_choices + tuple
searchabel_field_choices = searchabel_field_choices + (('', '--------'),)
shared_attrs = {
'autocomplete': 'off',
'class': 'form-control datetimepicker-input',
}
searchable_field = forms.ChoiceField(choices=searchabel_field_choices, required=False)
op_choices = () # Should always start with an empty operations list since field has not yet been chosen
operation = forms.ChoiceField(choices=op_choices, required=False)
# 2 is usually only ever used if a range is being specified
# Numeric
date1 = forms.DateField(required=False, widget=DatePicker(attrs=shared_attrs))
date2 = forms.DateField(required=False, widget=DatePicker(attrs=shared_attrs))
datetime1 = forms.DateTimeField(required=False, widget=DateTimePicker(attrs=shared_attrs))
datetime2 = forms.DateTimeField(required=False, widget=DateTimePicker(attrs=shared_attrs))
integer = forms.IntegerField(required=False)
# Bool
bool = forms.BooleanField(required=False)
# String
string = forms.CharField(required=False)