Jacq
Jacq

Reputation: 105

Dynamic ChoiceField unable to be validated in form

I have a form that's being given a dictionary of selection, it populates it correctly but on form submit it is not valid. When attempting to print errors, non_field_errors there are just blanks. When I am redirected to the form, now the choice field is populated by one choice and the csrf token from previous submit.

I've tried assigning choices in different ways such as self.fields['calendar'] = forms.ChoiceField(choices=choice_list) directly assign in a different way. self.fields['calendar'].choices = choice_list, a custom validator that ignores the validation, and inline debugging.

Form model:

class CalendarSelectionForm(forms.Form):
    calendar = forms.ChoiceField(label="Calendar")

    def __init__(self, calendars=None, *args, **kwargs):
        super(CalendarSelectionForm, self).__init__(*args, **kwargs)
        choice_list = [(calendar_id, calendar_name) for calendar_id, calendar_name in calendars.items()]
        if calendars:
            self.fields['calendar'].choices = choice_list

View:

    if request.method == "POST":
        print(request.POST)
        cal_sync_form = CalendarSelectionForm(request.POST)
        print("Non-field errors " + str(cal_sync_form.non_field_errors()))
        print("Reg form errors " + str(cal_sync_form.errors))
        # print("Field val " + str(cal_sync_form.calendar))
        print("Field data " + str(cal_sync_form.data))
        print("Field fields " + str(cal_sync_form.fields) + " Form is " + str(cal_sync_form.is_valid()))
        if cal_sync_form.is_valid():
            data = cal_sync_form.cleaned_data
            print(data)
            return render(request, 'management/gcal_sync_dashboard.html')
        else:
            return render(request, 'management/acct_select.html', {'form': cal_sync_form})

Form template:

<form class="form-import" action="/manage/gcal/sync/" method="post" id = "">
    {% csrf_token %}
    {{ form.calendar }}
    {{ form.errors }}
    {{ form.non_field_errors }}
    <div class="push clearfix"></div>
    <div class="col-sm-6 no-pad push"><input class="btn btn-brand btn-little button filson push-half" type="submit" value="Select email"><i class="fa fa-plus"></i></input>
  </div>
</form>

The goal is to validate a posted form, the current print statements print out

<QueryDict: {'csrfmiddlewaretoken': ['sJHE8JJAzmeS0nRjaYZg5KdMlevJiInYY0G4YFJeITH1cVjciIdR1Dq1N28loUIL'], 'calendar': ['[email protected]']}>
Non-field errors 
Reg form errors 
Field data {}
Field fields OrderedDict([('calendar', <django.forms.fields.ChoiceField object at 0x117323080>)]) Form is False

Upvotes: 2

Views: 329

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477834

In your view, you make a call to the CalendarSelectionForm constructor with request.POST as first positional argument. So that means that you call the __init__ function, and request.POST is passed as the calendars parameter.

You can fix this by constructing your form with named parameters. You furthermore will need to pass the same parameter to calendars as you did when you rendered the form with the GET request, since otherwise the choices do not per se match, and the user might have picked an option that is in that case not available during the POST request. Like:

if request.method == 'POST':
    cal_sync_form = CalendarSelectionForm(calendars=my_calendars, data=request.POST)
    # ...

with my_calendars the same value you pass when you constructed the form in the GET case.

Upvotes: 1

Related Questions