Difference between form Field "disabled" vs form widget attrs "disabled"?

Essentially, what is the difference between:

(1)
delay_time = forms.ChoiceField(choices=TIMES, required=False, disabled=True)

and

(2)
delay_time = forms.ChoiceField(choices=TIMES, required=False, widget=forms.Select(attrs={"disabled":"disabled"}))

?
I assumed there was no difference, and was using option (1). In my template I have some JS code which enables/disables the delay_time choice field successfully, whenever a user checks or unchecks a box. Note that this JS functionality works as expected in either case of options (1) and (2) - I inspected the HTML elements and it was identical in either case.
But with option (1), when the user submits the form, regardless of what option they chose for delay_time, an empty string gets sent back to the view, whereas for option (2) the correct delay_time string gets sent to view. I’m very confused why this is

The widget attribute will take the value inside the widget and append it to the dictionary being sent in the GET/POST request.

Taken from the docs at https://docs.djangoproject.com/en/3.0/ref/forms/widgets/: “A widget is Django’s representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget.”

If you do not add the widget option to your form field, the request sent upon selecting submit on your form will not know what value to append to the request.

Hope this helps :slight_smile:

Another option, if this delay_time is a model field, is to use forms.ModelField and create your widgets inside the Meta class.

class CustomForm(forms.ModelForm):
     class Meta:
          widgets = {
               'delay_time': forms.ChoiceField(...) # this may have syntax errors
           }

This behavior is expected, and is described in the disabled section of the form fields docs.

So in the first case, you’re defining the Form itself with a disabled field. Django is going to ignore all data submitted for that field. In the second case, the Form field itself is active - you’ve only set an HTML attribute on the widget, which is only going to affect the HTML.

As you’ve identified, JS can alter the behavior of the HTML on the page, but has no ability to affect how Django processes the form when the data is being submitted.

This clears things up a lot, I felt like I had it working but was unsatisfied that I didn’t understand why it was behaving that way, thanks a lot both of you!