Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

request.post_vars() method obliterates form keys with multiple values #2205

Open
simonw opened this issue Nov 5, 2023 · 3 comments
Open

Comments

@simonw
Copy link
Owner

simonw commented Nov 5, 2023

async def post_vars(self):
body = await self.post_body()
return dict(parse_qsl(body.decode("utf-8"), keep_blank_values=True))

In GET requests you can do ?foo=1&foo=2 - you can do the same in POST requests, but the dict() call here eliminates those duplicates.

You can't even try calling post_body() and implement your own custom parsing because of:

@simonw
Copy link
Owner Author

simonw commented Nov 5, 2023

I found this problem while trying to use WTForms with this pattern:

choices = [(col, col) for col in await db.table_columns(table)]

class ConfigForm(Form):
    template = TextAreaField("Template")
    api_token = PasswordField("OpenAI API token")
    columns = MultiCheckboxField('Columns', choices=choices)

@simonw
Copy link
Owner Author

simonw commented Nov 6, 2023

It should return a MultiParams:

class MultiParams:
def __init__(self, data):
# data is a dictionary of key => [list, of, values] or a list of [["key", "value"]] pairs
if isinstance(data, dict):
for key in data:
assert isinstance(
data[key], (list, tuple)
), "dictionary data should be a dictionary of key => [list]"
self._data = data
elif isinstance(data, list) or isinstance(data, tuple):
new_data = {}
for item in data:
assert (
isinstance(item, (list, tuple)) and len(item) == 2
), "list data should be a list of [key, value] pairs"
key, value = item
new_data.setdefault(key, []).append(value)
self._data = new_data

Change needs to be made before 1.0.

return MultiParams(urllib.parse.parse_qs(body.decode("utf-8")))

Need to remember why I was using keep_blank_values=True there and check that using MultiParams doesn't conflict with that reason.

@simonw
Copy link
Owner Author

simonw commented Nov 6, 2023

That keep_blank_values=True is from 0934844

Commit message:

request.post_vars() no longer discards empty values

Relevant test:

async def receive():
return {
"type": "http.request",
"body": b"foo=bar&baz=1&empty=",
"more_body": False,
}
request = Request(scope, receive)
assert {"foo": "bar", "baz": "1", "empty": ""} == await request.post_vars()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant