pallets-eco / wtforms

A flexible forms validation and rendering library for Python.
https://wtforms.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.51k stars 395 forks source link

Cannot update field data on numeric fields when formdata is processed #834

Open tspng opened 8 months ago

tspng commented 8 months ago

I think I encountered a bug related to how field values are rendered on numeric fields. I am trying to dynamically change some field values after the form has been submitted. It works on StringFields for example but not on IntegerFields.

Actual Behavior

from werkzeug.datastructures import ImmutableMultiDict
from wtforms import Form, IntegerField, StringField

class MyForm(Form):
    string_field = StringField("Name")
    integer_field = IntegerField("Value")

formdata = ImmutableMultiDict([("string_field", "World"), ("integer_field", "")])
form = MyForm(formdata=formdata)
assert form.string_field.data == "World"
assert form.integer_field.data is None

form.string_field.data = "Hello"
assert form.string_field.data == "Hello"
assert 'value="Hello"' in form.string_field()  # <- works

form.integer_field.data = 42
assert form.integer_field.data == 42
assert 'value="42"' in form.integer_field()  # <- AssertionError
Traceback (most recent call last):
  File "/tmp/test.py", line 21, in <module>
    assert 'value="42"' in form.integer_field()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Expected Behavior

Allow setting the field data on numeric fields after formdata is processed. So the behaviour is aligned with other non-numeric fields.

Problem

Right now, all (or most) numeric fields use raw_data[0] if present for the fields value in the rendered HTML output.

def _value(self):
    if self.raw_data:
        return self.raw_data[0]
    if self.data is not None:
        return str(self.data)
    return ""

Environment

EDIT: typo

tspng commented 8 months ago

I just saw that some of that behaviour has been previously discussed (https://github.com/wtforms/wtforms/issues/662). I can understand the rationale that by default the user input should be displayed as entered when the form is shown again.

But as far as I know, the field's value can't be changed if the field is re-rendered. Is that correct?

My (ugly) workaround is to set field.raw_data = None as well as field.data = <NEW VALUE>.