#34721 closed Bug (invalid)

ChoiceField/TypedChoiceField: .value() has inconsistent behaviour, coercion not applied.

Reported by: Daniel Owned by: nobody
Component: Forms Version: 4.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Daniel)

Given a form like

# forms.py
class MyForm(Form):
    colour = ChoiceField(
        choices=[(0, "Red"), (1, "Green"), (2, "Blue"), (3, "Yellow")],
        initial=0,
        widget=HiddenInput(),
        required=True,
    )

# views.py
def my_view(request):
    my_form = MyForm(request.POST or None)
...
    return render(request,"template.html", { "my_form": my_form })

and a template like

{% for colour_id, colour in my_form.fields.colour.choices %}
    {{ colour }}: {% if colour_id == my_form.colour.value %}selected{% else %}not selected{% endif %}
{% endfor %}

I find inconsistent behaviour in the return type of my_form["colour"], or {{ my_form.colour.value }} respectively.

my_form.fields["colour"].choices, and {{ my_form.fields.colour.choices }} correctly return the list of tuples assigned to the choices= parameter of the ChoiceField, retaining their types. Hence, colour_id and colour are of type int and string respectively.

If the form is unbound and the fields initial= value is used, then my_form["colour"], and {{ my_form.colour.value }} return the initial value of 0 as type int.

If the form is bound, then my_form["colour"], and {{ my_form.colour.value }} return the selected choice as type str.

I would expect that all, my_form.fields["colour"].choices and {{ my_form.fields.colour.choices }} and my_form["colour"] and {{ my_form.colour.value }} would return the values as the same type.

Changing ChoiceField(...) to TypedChoiceField(..., coerce=int) does only affect my_form.cleaned_data["colour"], but neither of my_form.fields["colour"].choices,{{ my_form.fields.colour.choices }}, or my_form["colour"], or{{ my_form.colour.value }}.

Ultimately leads to {% if colour_id == my_form.colour.value %} never being true when the form is bound, even when it should.

Attachments (1)

Sample.zip (18.1 KB ) - added by Daniel 16 months ago.
Sample Project

Download all attachments as: .zip

Change History (6)

comment:1 by Daniel, 16 months ago

Description: modified (diff)

comment:2 by Natalia Bidart, 16 months ago

Resolution: worksforme
Status: newclosed

Hello,

From my initial testing, and using as a base the code you shared, the template shows the expected behavior: when request.POST is empty, then data is None and I see Red as selected which is the default. If I hard code data to be, for example, {"colour": 3}, I see Yellow as selected.

Because of the above, the best place to get answers to your issue is using any of the user support channels from this link. Since the goal of this issue tracker is to track issues about Django itself, and your issue seems, at first, to be located in your custom code, I'll be closing this ticket as invalid.

If, after debugging, you find out that this is indeed a bug in Django, please re-open with the specific details.

Thank you!

by Daniel, 16 months ago

Attachment: Sample.zip added

Sample Project

in reply to:  2 comment:3 by Daniel, 16 months ago

Replying to Natalia Bidart:

Hello,

From my initial testing, and using as a base the code you shared, the template shows the expected behavior: when request.POST is empty, then data is None and I see Red as selected which is the default. If I hard code data to be, for example, {"colour": 3}, I see Yellow as selected.

Because of the above, the best place to get answers to your issue is using any of the user support channels from this link. Since the goal of this issue tracker is to track issues about Django itself, and your issue seems, at first, to be located in your custom code, I'll be closing this ticket as invalid.

If, after debugging, you find out that this is indeed a bug in Django, please re-open with the specific details.

Thank you!

Hi Natalia,

if you want specific details, please explain what you understand as specific as my description of the issue is very detailled accurate. You did not properly inspect the data. It is a bug.
A sample is attached.

Version 1, edited 16 months ago by Daniel (previous) (next) (diff)

comment:4 by Daniel, 16 months ago

Resolution: worksforme
Status: closednew

in reply to:  description comment:5 by Mariusz Felisiak, 16 months ago

Resolution: invalid
Status: newclosed

Changing ChoiceField(...) to TypedChoiceField(..., coerce=int) does only affect my_form.cleaned_data["colour"], but neither of my_form.fields["colour"].choices,{{ my_form.fields.colour.choices }}, or my_form["colour"], or{{ my_form.colour.value }}.

Yes, and that's exactly what you should use, TypedChoiceField and cleaned_data.

Ultimately leads to {% if colour_id == my_form.colour.value %} never being true when the form is bound, even when it should.

That's an incorrect expectation. BoundField.value() is the raw value of the field. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.

Note: See TracTickets for help on using tickets.
Back to Top