Skip to content

No way to tell that a given form is submitted #402

@davidism

Description

@davidism

Related to at least #119, #242, #267, #280, #291, #355, #401

We do if formdata is not None before calling process_formdata on each field in a form. This broke the rather common case where users were passing request.form to a form even if the method wasn't POST because request.form isn't None. Previously we checked bool(formdata), which handled this case but caused processing to behave inconsistently depending on what was submitted.

Flask-WTF somewhat handles this by only passing request.form if the method is actually a submit method (POST, PUT, DELETE).

None of the approaches are really correct. Maybe you have a client that always sends some extra data (for example, an auth key). Or you have two forms on the same page, whichever was not submitted will still see the same non-empty form data as the one that was submitted.

f1 = FormA(request.form)  # was submitted, sees data
f2 = FormB(request.form)  # was not submitted, sees same data

Most built-in fields mask this issue because they only change data (which is set from the default / object data first) if formdata.getlist('name') is not empty. However, StringField will set data = '' if nothing was submitted (which #355 changes). And SelectMultipleField and MultipleFileField will overwrite with an empty list because there's no way to distinguish "selected zero values" with "didn't send data" (BooleanField has a similar problem.)

This can also cause weird issues during validation with InputRequired. If there are two forms, and you do something like if f1.validate(): ... elif f2.validate(): ..., and f1 was not submitted, it will show a "input required" error even if a default value was being displayed, which is confusing.

I'm not sure what the solution is here, but am open to discussion. We can break things, I don't care about backwards compatibility for 3.0.

CC @lepture @alanhamlett

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactoringDoes the same thing but in a different way

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions